Skip to content

Commit f90f936

Browse files
committed
KVM: arm64: Rewrite IMPDEF PMU version as NI
KVM allows userspace to write an IMPDEF PMU version to the corresponding 32bit and 64bit ID register fields for the sake of backwards compatibility with kernels that lacked commit 3d0dba5 ("KVM: arm64: PMU: Move the ID_AA64DFR0_EL1.PMUver limit to VM creation"). Plumbing that IMPDEF PMU version through to the gues is getting in the way of progress, and really doesn't any sense in the first place. Bite the bullet and reinterpret the IMPDEF PMU version as NI (0) for userspace writes. Additionally, spill the dirty details into a comment. Link: https://lore.kernel.org/r/20230609190054.1542113-5-oliver.upton@linux.dev Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
1 parent 2251e9f commit f90f936

4 files changed

Lines changed: 38 additions & 27 deletions

File tree

arch/arm64/include/asm/kvm_host.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,7 @@ struct kvm_arch {
241241

242242
u8 pfr0_csv2;
243243
u8 pfr0_csv3;
244-
struct {
245-
u8 imp:4;
246-
u8 unimp:4;
247-
} dfr0_pmuver;
244+
u8 dfr0_pmuver;
248245

249246
/* Hypercall features firmware registers' descriptor */
250247
struct kvm_smccc_features smccc_feat;

arch/arm64/kvm/arm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
168168
* Initialise the default PMUver before there is a chance to
169169
* create an actual PMU.
170170
*/
171-
kvm->arch.dfr0_pmuver.imp = kvm_arm_pmu_get_pmuver_limit();
171+
kvm->arch.dfr0_pmuver = kvm_arm_pmu_get_pmuver_limit();
172172

173173
bitmap_zero(kvm->arch.vcpu_features, KVM_VCPU_MAX_FEATURES);
174174

arch/arm64/kvm/sys_regs.c

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,9 +1177,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
11771177
static u8 vcpu_pmuver(const struct kvm_vcpu *vcpu)
11781178
{
11791179
if (kvm_vcpu_has_pmu(vcpu))
1180-
return vcpu->kvm->arch.dfr0_pmuver.imp;
1180+
return vcpu->kvm->arch.dfr0_pmuver;
11811181

1182-
return vcpu->kvm->arch.dfr0_pmuver.unimp;
1182+
return 0;
11831183
}
11841184

11851185
static u8 perfmon_to_pmuver(u8 perfmon)
@@ -1384,18 +1384,35 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
13841384
bool valid_pmu;
13851385

13861386
host_pmuver = kvm_arm_pmu_get_pmuver_limit();
1387+
pmuver = SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, val);
1388+
1389+
/*
1390+
* Prior to commit 3d0dba5764b9 ("KVM: arm64: PMU: Move the
1391+
* ID_AA64DFR0_EL1.PMUver limit to VM creation"), KVM erroneously
1392+
* exposed an IMP_DEF PMU to userspace and the guest on systems w/
1393+
* non-architectural PMUs. Of course, PMUv3 is the only game in town for
1394+
* PMU virtualization, so the IMP_DEF value was rather user-hostile.
1395+
*
1396+
* At minimum, we're on the hook to allow values that were given to
1397+
* userspace by KVM. Cover our tracks here and replace the IMP_DEF value
1398+
* with a more sensible NI. The value of an ID register changing under
1399+
* the nose of the guest is unfortunate, but is certainly no more
1400+
* surprising than an ill-guided PMU driver poking at impdef system
1401+
* registers that end in an UNDEF...
1402+
*/
1403+
if (pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF) {
1404+
val &= ~ID_AA64DFR0_EL1_PMUVer_MASK;
1405+
pmuver = 0;
1406+
}
13871407

13881408
/*
13891409
* Allow AA64DFR0_EL1.PMUver to be set from userspace as long
1390-
* as it doesn't promise more than what the HW gives us. We
1391-
* allow an IMPDEF PMU though, only if no PMU is supported
1392-
* (KVM backward compatibility handling).
1410+
* as it doesn't promise more than what the HW gives us.
13931411
*/
1394-
pmuver = FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), val);
1395-
if ((pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF && pmuver > host_pmuver))
1412+
if (pmuver > host_pmuver)
13961413
return -EINVAL;
13971414

1398-
valid_pmu = (pmuver != 0 && pmuver != ID_AA64DFR0_EL1_PMUVer_IMP_DEF);
1415+
valid_pmu = pmuver;
13991416

14001417
/* Make sure view register and PMU support do match */
14011418
if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
@@ -1407,11 +1424,7 @@ static int set_id_aa64dfr0_el1(struct kvm_vcpu *vcpu,
14071424
if (val)
14081425
return -EINVAL;
14091426

1410-
if (valid_pmu)
1411-
vcpu->kvm->arch.dfr0_pmuver.imp = pmuver;
1412-
else
1413-
vcpu->kvm->arch.dfr0_pmuver.unimp = pmuver;
1414-
1427+
vcpu->kvm->arch.dfr0_pmuver = pmuver;
14151428
return 0;
14161429
}
14171430

@@ -1423,19 +1436,24 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
14231436
bool valid_pmu;
14241437

14251438
host_perfmon = pmuver_to_perfmon(kvm_arm_pmu_get_pmuver_limit());
1439+
perfmon = SYS_FIELD_GET(ID_DFR0_EL1, PerfMon, val);
1440+
1441+
if (perfmon == ID_DFR0_EL1_PerfMon_IMPDEF) {
1442+
val &= ~ID_DFR0_EL1_PerfMon_MASK;
1443+
perfmon = 0;
1444+
}
14261445

14271446
/*
14281447
* Allow DFR0_EL1.PerfMon to be set from userspace as long as
14291448
* it doesn't promise more than what the HW gives us on the
14301449
* AArch64 side (as everything is emulated with that), and
14311450
* that this is a PMUv3.
14321451
*/
1433-
perfmon = FIELD_GET(ARM64_FEATURE_MASK(ID_DFR0_EL1_PerfMon), val);
1434-
if ((perfmon != ID_DFR0_EL1_PerfMon_IMPDEF && perfmon > host_perfmon) ||
1452+
if (perfmon > host_perfmon ||
14351453
(perfmon != 0 && perfmon < ID_DFR0_EL1_PerfMon_PMUv3))
14361454
return -EINVAL;
14371455

1438-
valid_pmu = (perfmon != 0 && perfmon != ID_DFR0_EL1_PerfMon_IMPDEF);
1456+
valid_pmu = perfmon;
14391457

14401458
/* Make sure view register and PMU support do match */
14411459
if (kvm_vcpu_has_pmu(vcpu) != valid_pmu)
@@ -1447,11 +1465,7 @@ static int set_id_dfr0_el1(struct kvm_vcpu *vcpu,
14471465
if (val)
14481466
return -EINVAL;
14491467

1450-
if (valid_pmu)
1451-
vcpu->kvm->arch.dfr0_pmuver.imp = perfmon_to_pmuver(perfmon);
1452-
else
1453-
vcpu->kvm->arch.dfr0_pmuver.unimp = perfmon_to_pmuver(perfmon);
1454-
1468+
vcpu->kvm->arch.dfr0_pmuver = perfmon_to_pmuver(perfmon);
14551469
return 0;
14561470
}
14571471

include/kvm/arm_pmu.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu);
9393
* Evaluates as true when emulating PMUv3p5, and false otherwise.
9494
*/
9595
#define kvm_pmu_is_3p5(vcpu) \
96-
(vcpu->kvm->arch.dfr0_pmuver.imp >= ID_AA64DFR0_EL1_PMUVer_V3P5)
96+
(vcpu->kvm->arch.dfr0_pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P5)
9797

9898
u8 kvm_arm_pmu_get_pmuver_limit(void);
9999

0 commit comments

Comments
 (0)