Skip to content

Commit 6685f5d

Browse files
James Morseoupton
authored andcommitted
KVM: arm64: Disable MPAM visibility by default and ignore VMM writes
commit 011e5f5 ("arm64/cpufeature: Add remaining feature bits in ID_AA64PFR0 register") exposed the MPAM field of AA64PFR0_EL1 to guests, but didn't add trap handling. A previous patch supplied the missing trap handling. Existing VMs that have the MPAM field of ID_AA64PFR0_EL1 set need to be migratable, but there is little point enabling the MPAM CPU interface on new VMs until there is something a guest can do with it. Clear the MPAM field from the guest's ID_AA64PFR0_EL1 and on hardware that supports MPAM, politely ignore the VMMs attempts to set this bit. Guests exposed to this bug have the sanitised value of the MPAM field, so only the correct value needs to be ignored. This means the field can continue to be used to block migration to incompatible hardware (between MPAM=1 and MPAM=5), and the VMM can't rely on the field being ignored. Signed-off-by: James Morse <james.morse@arm.com> Co-developed-by: Joey Gouly <joey.gouly@arm.com> Signed-off-by: Joey Gouly <joey.gouly@arm.com> Reviewed-by: Gavin Shan <gshan@redhat.com> Tested-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> Reviewed-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20241030160317.2528209-7-joey.gouly@arm.com Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
1 parent 7da540e commit 6685f5d

File tree

1 file changed

+42
-3
lines changed

1 file changed

+42
-3
lines changed

arch/arm64/kvm/sys_regs.c

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,7 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu,
15441544
val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MTEX);
15451545
val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_DF2);
15461546
val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_PFAR);
1547+
val &= ~ARM64_FEATURE_MASK(ID_AA64PFR1_EL1_MPAM_frac);
15471548
break;
15481549
case SYS_ID_AA64PFR2_EL1:
15491550
/* We only expose FPMR */
@@ -1730,6 +1731,13 @@ static u64 sanitise_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, u64 val)
17301731

17311732
val &= ~ID_AA64PFR0_EL1_AMU_MASK;
17321733

1734+
/*
1735+
* MPAM is disabled by default as KVM also needs a set of PARTID to
1736+
* program the MPAMVPMx_EL2 PARTID remapping registers with. But some
1737+
* older kernels let the guest see the ID bit.
1738+
*/
1739+
val &= ~ID_AA64PFR0_EL1_MPAM_MASK;
1740+
17331741
return val;
17341742
}
17351743

@@ -1838,9 +1846,39 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
18381846
}
18391847

18401848
static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu,
1841-
const struct sys_reg_desc *rd, u64 val)
1849+
const struct sys_reg_desc *rd, u64 user_val)
18421850
{
1843-
return set_id_reg(vcpu, rd, val);
1851+
u64 hw_val = read_sanitised_ftr_reg(SYS_ID_AA64PFR0_EL1);
1852+
u64 mpam_mask = ID_AA64PFR0_EL1_MPAM_MASK;
1853+
1854+
/*
1855+
* Commit 011e5f5bf529f ("arm64/cpufeature: Add remaining feature bits
1856+
* in ID_AA64PFR0 register") exposed the MPAM field of AA64PFR0_EL1 to
1857+
* guests, but didn't add trap handling. KVM doesn't support MPAM and
1858+
* always returns an UNDEF for these registers. The guest must see 0
1859+
* for this field.
1860+
*
1861+
* But KVM must also accept values from user-space that were provided
1862+
* by KVM. On CPUs that support MPAM, permit user-space to write
1863+
* the sanitizied value to ID_AA64PFR0_EL1.MPAM, but ignore this field.
1864+
*/
1865+
if ((hw_val & mpam_mask) == (user_val & mpam_mask))
1866+
user_val &= ~ID_AA64PFR0_EL1_MPAM_MASK;
1867+
1868+
return set_id_reg(vcpu, rd, user_val);
1869+
}
1870+
1871+
static int set_id_aa64pfr1_el1(struct kvm_vcpu *vcpu,
1872+
const struct sys_reg_desc *rd, u64 user_val)
1873+
{
1874+
u64 hw_val = read_sanitised_ftr_reg(SYS_ID_AA64PFR1_EL1);
1875+
u64 mpam_mask = ID_AA64PFR1_EL1_MPAM_frac_MASK;
1876+
1877+
/* See set_id_aa64pfr0_el1 for comment about MPAM */
1878+
if ((hw_val & mpam_mask) == (user_val & mpam_mask))
1879+
user_val &= ~ID_AA64PFR1_EL1_MPAM_frac_MASK;
1880+
1881+
return set_id_reg(vcpu, rd, user_val);
18441882
}
18451883

18461884
/*
@@ -2399,7 +2437,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
23992437
ID_AA64PFR0_EL1_RAS |
24002438
ID_AA64PFR0_EL1_AdvSIMD |
24012439
ID_AA64PFR0_EL1_FP)),
2402-
ID_WRITABLE(ID_AA64PFR1_EL1, ~(ID_AA64PFR1_EL1_PFAR |
2440+
ID_FILTERED(ID_AA64PFR1_EL1, id_aa64pfr1_el1,
2441+
~(ID_AA64PFR1_EL1_PFAR |
24032442
ID_AA64PFR1_EL1_DF2 |
24042443
ID_AA64PFR1_EL1_MTEX |
24052444
ID_AA64PFR1_EL1_THE |

0 commit comments

Comments
 (0)