KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
Fuad Tabba
reviewerPATCH 1/1 KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
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.
---
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(-)
arch/arm64/include/asm/cpufeature.h +5 -0
@@ -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 );
arch/arm64/kernel/cpufeature.c +13 -0
@@ -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,
arch/arm64/kvm/hyp/include/hyp/switch.h +2 -2
@@ -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);
arch/arm64/tools/cpucaps +1 -0
@@ -78,6 +78,7 @@ KVM_PROTECTED_MODE MISMATCHED_CACHE_TYPE MPAM MPAM_HCR +MPAM_SYSREGS MTE MTE_ASYMM MTE_FAR
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
Signature · 1 line
2.39.5
[email protected]
AI reviewThank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] KVM fails to trap guest accesses to MPAM registers when the sanitized MPAM capability evaluates to false (e.g., due to arm64.nompam boot parameter or asymmetric CPU topologies), allowing the guest to directly access physical host MPAM hardware. --
commit 4af5e7631ab3a7072311a3251a8c2768e198ef2e
Author: Fuad Tabba <[email protected]>
KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
This patch updates KVM to trap guest accesses to MPAM registers based solely on the ID registers rather than the ARM64_MPAM capability, which also requires MPAMEN to be set.
Quoted 5 lines
arch/arm64/kernel/cpufeature.c +0
-0
arch/arm64/kernel/cpufeature.c test_has_mpam(const struct arm64_cpu_capabilities *entry, int scope) · line 2510
Quoted 10 lines
arch/arm64/kernel/cpufeature.c +7
-0@@ -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 (); +} +
[Severity: High]
This isn't a bug introduced by this patch, but it appears to be a pre-existing issue. Does relying on detect_ftr_has_mpam() here leave the physical MPAM hardware exposed?
The detect_ftr_has_mpam() function checks the sanitised CPU feature registers. If the host is booted with the arm64.nompam parameter, or on systems with asymmetric CPU topologies where the boot CPU lacks MPAM, this check will evaluate to false even if the physical hardware on the current CPU implements MPAM.
[ ... ]
arch/arm64/kvm/hyp/include/hyp/switch.h static inline void __activate_traps_mpam(struct kvm_vcpu *vcpu) · line 304
Quoted 13 lines
arch/arm64/kvm/hyp/include/hyp/switch.h +1
-1@@ -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 */
[Severity: High]
This is also part of the pre-existing issue mentioned above.
When system_supports_mpam_sysregs() returns false due to the sanitised feature registers, this early return skips setting the TRAPMPAM0EL1 and TRAPMPAM1EL1 trap bits in SYS_MPAM2_EL2.
Since the hardware traps remain disabled, could this allow a malicious guest to directly access physical MPAM configuration registers (such as MPAM0_EL1) and bypass hypervisor isolation? This might allow the guest to manipulate host cache and memory bandwidth partitioning, potentially causing a denial of service against the host or other VMs.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Fuad Tabba
reviewerQuoted 5 lines · On Thu, 3 Sept 2026 at 17:25, <[email protected]> wrote:
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] KVM fails to trap guest accesses to MPAM registers when the sanitized MPAM capability evaluates to false (e.g., due toarm64.nompamboot parameter or asymmetric CPU topologies), allowing the guest to directly access physical host MPAM hardware.
See : https://lore.kernel.org/all/[email protected]/
/fuad
Quoted 71 lines
--
commit
4af5e7631ab3a7072311a3251a8c2768e198ef2e
Author: Fuad Tabba <[email protected]>KVM: arm64: Trap guest MPAM accesses whenever MPAM is implemented
This patch updates KVM to trap guest accesses to MPAM registers based solely on the ID registers rather than the
ARM64_MPAMcapability, which also requires MPAMEN to be set.Quoted 5 lines
arch/arm64/kernel/cpufeature.c +0
-0arch/arm64/kernel/cpufeature.c test_has_mpam(const struct arm64_cpu_capabilities *entry, int scope) · line 2510
Quoted 10 lines
arch/arm64/kernel/cpufeature.c +7
-0@@ -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 (); +} +[Severity: High]
This isn't a bug introduced by this patch, but it appears to be a pre-existing issue. Does relying ondetect_ftr_has_mpam()here leave the physical MPAM hardware exposed?The
detect_ftr_has_mpam()function checks the sanitised CPU feature registers. If the host is booted with the arm64.nompam parameter, or on systems with asymmetric CPU topologies where the boot CPU lacks MPAM, this check will evaluate to false even if the physical hardware on the current CPU implements MPAM.[ ... ]
arch/arm64/kvm/hyp/include/hyp/switch.h static inline void __activate_traps_mpam(struct kvm_vcpu *vcpu) · line 304
Quoted 13 lines
arch/arm64/kvm/hyp/include/hyp/switch.h +1
-1@@ -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 */ [Severity: High]
This is also part of the pre-existing issue mentioned above.When
system_supports_mpam_sysregs()returns false due to the sanitised feature registers, this early return skips setting the TRAPMPAM0EL1 and TRAPMPAM1EL1 trap bits inSYS_MPAM2_EL2.Since the hardware traps remain disabled, could this allow a malicious guest to directly access physical MPAM configuration registers (such as
MPAM0_EL1) and bypass hypervisor isolation? This might allow the guest to manipulate host cache and memory bandwidth partitioning, potentially causing a denial of service against the host or other VMs.--
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Quoted 45 lines · Fuad Tabba
finalise_el2_state()clears the EL2 MPAM traps whenever the ID registers advertise MPAM, but KVM sets them only whenARM64_MPAMis 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_statealready wroteMPAM2_EL2under 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 <[email protected]>---
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(-)arch/arm64/include/asm/cpufeature.h +5
-0@@ -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 );arch/arm64/kernel/cpufeature.c +0
-0
arch/arm64/kernel/cpufeature.c test_has_mpam(const struct arm64_cpu_capabilities *entry, int scope) · line 2503
Quoted 3 lines
arch/arm64/kernel/cpufeature.c +0
-0@@ -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,
arch/arm64/kernel/cpufeature.c test_has_mpam(const struct arm64_cpu_capabilities *entry, int scope) · line 2509
Quoted 6 lines
arch/arm64/kernel/cpufeature.c +6
-0+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 ?
Quoted 56 lines
arch/arm64/kernel/cpufeature.c +7
-0+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,arch/arm64/kvm/hyp/include/hyp/switch.h +2
-2@@ -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);arch/arm64/tools/cpucaps +1
-0@@ -78,6 +78,7 @@ KVM_PROTECTED_MODE MISMATCHED_CACHE_TYPE MPAM MPAM_HCR +MPAM_SYSREGS MTE MTE_ASYMM MTE_FARbase-commit:
cee9395acd8043be0644b25c34bfa86623f2b935--
2.39.5
Fuad Tabba
reviewerHi Yuan,
Quoted 13 lines
arch/arm64/kernel/cpufeature.c test_has_mpam(const struct arm64_cpu_capabilities *entry, int scope) · line 2509
Quoted 6 lines
arch/arm64/kernel/cpufeature.c +6
-0+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 thatSYS_MPAM2_EL2andSYS_MPAMHCR_EL2are configured not trap anything by firmware, thus guest can still access MPAM registers.
Quoted 2 lines
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
Yao Yuan
Reviewed-byQuoted 15 lines · Fuad Tabba
Hi Yuan,
Quoted 13 lines
arch/arm64/kernel/cpufeature.c test_has_mpam(const struct arm64_cpu_capabilities *entry, int scope) · line 2509
Quoted 6 lines
arch/arm64/kernel/cpufeature.c +6
-0+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 thatSYS_MPAM2_EL2andSYS_MPAMHCR_EL2are configured not trap anything by firmware, thus guest can still access MPAM registers.
Quoted 7 lines
Quoted 2 lines
Do we need check the raw id register values for the real support state of MPAM here ?
Under arm64.nompam,
finalise_el2_stateskips theMPAM2_EL2andMPAMHCR_EL2writes too, via the samecheck_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.
Quoted 2 lines
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.
Quoted 6 lines
Gating on the raw ID registers would make
__activate_traps_mpam()writeMPAM2_EL2on 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:
Quoted 7 lines
The arm64.nompam hazard is documented separately [1].
Cheers,
/fuad
Hi Fuad,
Quoted 5 lines · Fuad Tabba
finalise_el2_state()clears the EL2 MPAM traps whenever the ID registers advertise MPAM, but KVM sets them only whenARM64_MPAMis 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.
Quoted 24 lines
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_statealready wroteMPAM2_EL2under 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 <[email protected]>---
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(-)arch/arm64/include/asm/cpufeature.h +0
-0
arch/arm64/include/asm/cpufeature.h static __always_inline bool system_supports_mpam_hcr(void) · line 879
Quoted 7 lines
arch/arm64/include/asm/cpufeature.h +4
-0@@ -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
Quoted 71 lines
arch/arm64/include/asm/cpufeature.h +1
-0+static inline bool system_supports_pmuv3 (void ) {return cpus_have_final_cap (ARM64_HAS_PMUV3 );arch/arm64/kernel/cpufeature.c +13
-0@@ -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,arch/arm64/kvm/hyp/include/hyp/switch.h +2
-2@@ -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);arch/arm64/tools/cpucaps +1
-0@@ -78,6 +78,7 @@ KVM_PROTECTED_MODE MISMATCHED_CACHE_TYPE MPAM MPAM_HCR +MPAM_SYSREGS MTE MTE_ASYMM MTE_FARbase-commit:
cee9395acd8043be0644b25c34bfa86623f2b935
Fuad Tabba
reviewerHi Ben,
On Mon, 7 Sept 2026 at 10:12, Ben Horgan <[email protected]> wrote:
...
Quoted 8 lines
Quoted 8 lines
arch/arm64/include/asm/cpufeature.h +0
-0@@ -873,6 +873,11 @@ static __always_inline bool system_supports_mpam_hcr(void)return alternative_has_cap_unlikely (ARM64_MPAM_HCR ); }
Quoted 16 lines
arch/arm64/include/asm/cpufeature.h static __always_inline bool system_supports_mpam_hcr(void) · line 879
Quoted 4 lines
arch/arm64/include/asm/cpufeature.h +4
-0+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_EL1etc, 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 distinguishsystem_supports_mpam_sysregs()andsystem_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/
Quoted 78 lines
Thanks,
Ben
Quoted 71 lines
arch/arm64/include/asm/cpufeature.h +1
-0+static inline bool system_supports_pmuv3 (void ) {return cpus_have_final_cap (ARM64_HAS_PMUV3 );arch/arm64/kernel/cpufeature.c +13
-0@@ -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,arch/arm64/kvm/hyp/include/hyp/switch.h +2
-2@@ -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);arch/arm64/tools/cpucaps +1
-0@@ -78,6 +78,7 @@ KVM_PROTECTED_MODE MISMATCHED_CACHE_TYPE MPAM MPAM_HCR +MPAM_SYSREGS MTE MTE_ASYMM MTE_FARbase-commit:
cee9395acd8043be0644b25c34bfa86623f2b935
Fuad Tabba
reviewerHi Ben,
I built the respin first, and it didn't hold up.
On Mon, 7 Sept 2026 at 11:28, Fuad Tabba <[email protected]> wrote:
...
Quoted 9 lines
This patch misses that case.
check_overridereads the CPU's own ID registers,detect_ftr_has_mpam()the sanitised ones, sofinalise_el2_stateclears 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 whetherMPAM2_EL2can be touched. KVM then needs onlysystem_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.
Quoted 4 lines
The one host left is a forced nVHE, where test_has_mpam()'s
MPAM1_EL1read would trap beforecpu_enable_mpam(). The respin makes the caps false whenis_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.
Quoted 1 line
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
Quoted 84 lines
Cheers,
/fuad[1] https://lore.kernel.org/all/apqrky27mJmUV9UA@willie-the-truck/
Quoted 78 lines
Thanks,
Ben
Quoted 71 lines
arch/arm64/include/asm/cpufeature.h +1
-0+static inline bool system_supports_pmuv3 (void ) {return cpus_have_final_cap (ARM64_HAS_PMUV3 );arch/arm64/kernel/cpufeature.c +13
-0@@ -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,arch/arm64/kvm/hyp/include/hyp/switch.h +2
-2@@ -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);arch/arm64/tools/cpucaps +1
-0@@ -78,6 +78,7 @@ KVM_PROTECTED_MODE MISMATCHED_CACHE_TYPE MPAM MPAM_HCR +MPAM_SYSREGS MTE MTE_ASYMM MTE_FARbase-commit:
cee9395acd8043be0644b25c34bfa86623f2b935
Hi Fuad,
Quoted 34 lines · Fuad Tabba
Hi Ben,
I built the respin first, and it didn't hold up.
On Mon, 7 Sept 2026 at 11:28, Fuad Tabba <[email protected]> wrote:
...Quoted 9 lines
This patch misses that case.
check_overridereads the CPU's own ID registers,detect_ftr_has_mpam()the sanitised ones, sofinalise_el2_stateclears 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 whetherMPAM2_EL2can be touched. KVM then needs onlysystem_supports_mpam(), and the cap goes.
finalise_el2_statecan't set that default. Under nVHE and hVHE the host is the trapped party, and cpufeature readsMPAMIDR_EL1andMPAM1_EL1from EL1 before KVM exists, so the only way out is to stop the host detecting MPAM.Quoted 4 lines
The one host left is a forced nVHE, where test_has_mpam()'s
MPAM1_EL1read would trap beforecpu_enable_mpam(). The respin makes the caps false whenis_hyp_nvhe(), so MPAM is off there rather than the boot hanging [1].
is_hyp_nvhe()is true on an hVHE host too, sinceis_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.Quoted 1 line
I'll respin along those lines.
Quoted 8 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
Quoted 88 lines
Cheers,
/fuadQuoted 84 lines
Cheers,
/fuad[1] https://lore.kernel.org/all/apqrky27mJmUV9UA@willie-the-truck/
Quoted 78 lines
Thanks,
Ben
Quoted 71 lines
arch/arm64/include/asm/cpufeature.h +1
-0+static inline bool system_supports_pmuv3 (void ) {return cpus_have_final_cap (ARM64_HAS_PMUV3 );arch/arm64/kernel/cpufeature.c +13
-0@@ -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,arch/arm64/kvm/hyp/include/hyp/switch.h +2
-2@@ -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);arch/arm64/tools/cpucaps +1
-0@@ -78,6 +78,7 @@ KVM_PROTECTED_MODE MISMATCHED_CACHE_TYPE MPAM MPAM_HCR +MPAM_SYSREGS MTE MTE_ASYMM MTE_FARbase-commit:
cee9395acd8043be0644b25c34bfa86623f2b935