Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: arm64: PMU: Allow userspace to limit PMCR_EL0.N for the guest

KVM does not yet support userspace modifying PMCR_EL0.N (With
the previous patch, KVM ignores what is written by userspace).
Add support userspace limiting PMCR_EL0.N.

Disallow userspace to set PMCR_EL0.N to a value that is greater
than the host value as KVM doesn't support more event counters
than what the host HW implements. Also, make this register
immutable after the VM has started running. To maintain the
existing expectations, instead of returning an error, KVM
returns a success for these two cases.

Finally, ignore writes to read-only bits that are cleared on
vCPU reset, and RES{0,1} bits (including writable bits that
KVM doesn't support yet), as those bits shouldn't be modified
(at least with the current KVM).

Co-developed-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Reiji Watanabe <reijiw@google.com>
Signed-off-by: Raghavendra Rao Ananta <rananta@google.com>
Link: https://lore.kernel.org/r/20231020214053.2144305-8-rananta@google.com
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>

authored by

Reiji Watanabe and committed by
Oliver Upton
ea9ca904 27131b19

+42 -4
+42 -4
arch/arm64/kvm/sys_regs.c
··· 719 719 720 720 static u64 reset_pmu_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) 721 721 { 722 - u64 n, mask = BIT(ARMV8_PMU_CYCLE_IDX); 722 + u64 mask = BIT(ARMV8_PMU_CYCLE_IDX); 723 + u8 n = vcpu->kvm->arch.pmcr_n; 723 724 724 - n = vcpu->kvm->arch.pmcr_n; 725 725 if (n) 726 726 mask |= GENMASK(n - 1, 0); 727 727 ··· 1133 1133 u64 *val) 1134 1134 { 1135 1135 *val = kvm_vcpu_read_pmcr(vcpu); 1136 + return 0; 1137 + } 1138 + 1139 + static int set_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, 1140 + u64 val) 1141 + { 1142 + u8 new_n = (val >> ARMV8_PMU_PMCR_N_SHIFT) & ARMV8_PMU_PMCR_N_MASK; 1143 + struct kvm *kvm = vcpu->kvm; 1144 + 1145 + mutex_lock(&kvm->arch.config_lock); 1146 + 1147 + /* 1148 + * The vCPU can't have more counters than the PMU hardware 1149 + * implements. Ignore this error to maintain compatibility 1150 + * with the existing KVM behavior. 1151 + */ 1152 + if (!kvm_vm_has_ran_once(kvm) && 1153 + new_n <= kvm_arm_pmu_get_max_counters(kvm)) 1154 + kvm->arch.pmcr_n = new_n; 1155 + 1156 + mutex_unlock(&kvm->arch.config_lock); 1157 + 1158 + /* 1159 + * Ignore writes to RES0 bits, read only bits that are cleared on 1160 + * vCPU reset, and writable bits that KVM doesn't support yet. 1161 + * (i.e. only PMCR.N and bits [7:0] are mutable from userspace) 1162 + * The LP bit is RES0 when FEAT_PMUv3p5 is not supported on the vCPU. 1163 + * But, we leave the bit as it is here, as the vCPU's PMUver might 1164 + * be changed later (NOTE: the bit will be cleared on first vCPU run 1165 + * if necessary). 1166 + */ 1167 + val &= ARMV8_PMU_PMCR_MASK; 1168 + 1169 + /* The LC bit is RES1 when AArch32 is not supported */ 1170 + if (!kvm_supports_32bit_el0()) 1171 + val |= ARMV8_PMU_PMCR_LC; 1172 + 1173 + __vcpu_sys_reg(vcpu, r->reg) = val; 1136 1174 return 0; 1137 1175 } 1138 1176 ··· 2239 2201 { SYS_DESC(SYS_CTR_EL0), access_ctr }, 2240 2202 { SYS_DESC(SYS_SVCR), undef_access }, 2241 2203 2242 - { PMU_SYS_REG(PMCR_EL0), .access = access_pmcr, 2243 - .reset = reset_pmcr, .reg = PMCR_EL0, .get_user = get_pmcr }, 2204 + { PMU_SYS_REG(PMCR_EL0), .access = access_pmcr, .reset = reset_pmcr, 2205 + .reg = PMCR_EL0, .get_user = get_pmcr, .set_user = set_pmcr }, 2244 2206 { PMU_SYS_REG(PMCNTENSET_EL0), 2245 2207 .access = access_pmcnten, .reg = PMCNTENSET_EL0, 2246 2208 .get_user = get_pmreg, .set_user = set_pmreg },