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

KVM: arm64: Add timer UAPI workaround to sysreg infrastructure

Amongst the numerous bugs that plague the KVM/arm64 UAPI, one of
the most annoying thing is that the userspace view of the virtual
timer has its CVAL and CNT encodings swapped.

In order to reduce the amount of code that has to know about this,
start by adding handling for this bug in the sys_reg code.

Nothing is making use of it yet, as the code responsible for userspace
interaction is catching the accesses early.

Signed-off-by: Marc Zyngier <maz@kernel.org>

+36 -3
+30 -3
arch/arm64/kvm/sys_regs.c
··· 5231 5231 } 5232 5232 } 5233 5233 5234 + static u64 kvm_one_reg_to_id(const struct kvm_one_reg *reg) 5235 + { 5236 + switch(reg->id) { 5237 + case KVM_REG_ARM_TIMER_CVAL: 5238 + return TO_ARM64_SYS_REG(CNTV_CVAL_EL0); 5239 + case KVM_REG_ARM_TIMER_CNT: 5240 + return TO_ARM64_SYS_REG(CNTVCT_EL0); 5241 + default: 5242 + return reg->id; 5243 + } 5244 + } 5245 + 5234 5246 int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg, 5235 5247 const struct sys_reg_desc table[], unsigned int num) 5236 5248 { 5237 5249 u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr; 5238 5250 const struct sys_reg_desc *r; 5251 + u64 id = kvm_one_reg_to_id(reg); 5239 5252 u64 val; 5240 5253 int ret; 5241 5254 5242 - r = id_to_sys_reg_desc(vcpu, reg->id, table, num); 5255 + r = id_to_sys_reg_desc(vcpu, id, table, num); 5243 5256 if (!r || sysreg_hidden(vcpu, r)) 5244 5257 return -ENOENT; 5245 5258 ··· 5285 5272 { 5286 5273 u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr; 5287 5274 const struct sys_reg_desc *r; 5275 + u64 id = kvm_one_reg_to_id(reg); 5288 5276 u64 val; 5289 5277 int ret; 5290 5278 5291 5279 if (get_user(val, uaddr)) 5292 5280 return -EFAULT; 5293 5281 5294 - r = id_to_sys_reg_desc(vcpu, reg->id, table, num); 5282 + r = id_to_sys_reg_desc(vcpu, id, table, num); 5295 5283 if (!r || sysreg_hidden(vcpu, r)) 5296 5284 return -ENOENT; 5297 5285 ··· 5352 5338 5353 5339 static bool copy_reg_to_user(const struct sys_reg_desc *reg, u64 __user **uind) 5354 5340 { 5341 + u64 idx; 5342 + 5355 5343 if (!*uind) 5356 5344 return true; 5357 5345 5358 - if (put_user(sys_reg_to_index(reg), *uind)) 5346 + switch (reg_to_encoding(reg)) { 5347 + case SYS_CNTV_CVAL_EL0: 5348 + idx = KVM_REG_ARM_TIMER_CVAL; 5349 + break; 5350 + case SYS_CNTVCT_EL0: 5351 + idx = KVM_REG_ARM_TIMER_CNT; 5352 + break; 5353 + default: 5354 + idx = sys_reg_to_index(reg); 5355 + } 5356 + 5357 + if (put_user(idx, *uind)) 5359 5358 return false; 5360 5359 5361 5360 (*uind)++;
+6
arch/arm64/kvm/sys_regs.h
··· 257 257 (val); \ 258 258 }) 259 259 260 + #define TO_ARM64_SYS_REG(r) ARM64_SYS_REG(sys_reg_Op0(SYS_ ## r), \ 261 + sys_reg_Op1(SYS_ ## r), \ 262 + sys_reg_CRn(SYS_ ## r), \ 263 + sys_reg_CRm(SYS_ ## r), \ 264 + sys_reg_Op2(SYS_ ## r)) 265 + 260 266 #endif /* __ARM64_KVM_SYS_REGS_LOCAL_H__ */