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

KVM: arm64: timers: Use CNTPOFF_EL2 to offset the physical timer

With ECV and CNTPOFF_EL2, it is very easy to offer an offset for
the physical timer. So let's do just that.

Nothing can set the offset yet, so this should have no effect
whatsoever (famous last words...).

Reviewed-by: Colton Lewis <coltonlewis@google.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20230330174800.2677007-5-maz@kernel.org

+21 -2
+17 -1
arch/arm64/kvm/arch_timer.c
··· 52 52 struct arch_timer_context *timer, 53 53 enum kvm_arch_timer_regs treg); 54 54 55 + static bool has_cntpoff(void) 56 + { 57 + return (has_vhe() && cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF)); 58 + } 59 + 55 60 u32 timer_get_ctl(struct arch_timer_context *ctxt) 56 61 { 57 62 struct kvm_vcpu *vcpu = ctxt->vcpu; ··· 89 84 90 85 static u64 timer_get_offset(struct arch_timer_context *ctxt) 91 86 { 92 - if (ctxt->offset.vm_offset) 87 + if (ctxt && ctxt->offset.vm_offset) 93 88 return *ctxt->offset.vm_offset; 94 89 95 90 return 0; ··· 437 432 kvm_call_hyp(__kvm_timer_set_cntvoff, cntvoff); 438 433 } 439 434 435 + static void set_cntpoff(u64 cntpoff) 436 + { 437 + if (has_cntpoff()) 438 + write_sysreg_s(cntpoff, SYS_CNTPOFF_EL2); 439 + } 440 + 440 441 static void timer_save_state(struct arch_timer_context *ctx) 441 442 { 442 443 struct arch_timer_cpu *timer = vcpu_timer(ctx->vcpu); ··· 491 480 write_sysreg_el0(0, SYS_CNTP_CTL); 492 481 isb(); 493 482 483 + set_cntpoff(0); 494 484 break; 495 485 case NR_KVM_TIMERS: 496 486 BUG(); ··· 562 550 write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTV_CTL); 563 551 break; 564 552 case TIMER_PTIMER: 553 + set_cntpoff(timer_get_offset(ctx)); 565 554 write_sysreg_el0(timer_get_cval(ctx), SYS_CNTP_CVAL); 566 555 isb(); 567 556 write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTP_CTL); ··· 780 767 vtimer->vcpu = vcpu; 781 768 vtimer->offset.vm_offset = &vcpu->kvm->arch.timer_data.voffset; 782 769 ptimer->vcpu = vcpu; 770 + ptimer->offset.vm_offset = &vcpu->kvm->arch.timer_data.poffset; 783 771 784 772 /* Synchronize cntvoff across all vtimers of a VM. */ 785 773 timer_set_offset(vtimer, kvm_phys_timer_read()); ··· 1311 1297 val = read_sysreg(cnthctl_el2); 1312 1298 val |= (CNTHCTL_EL1PCEN << cnthctl_shift); 1313 1299 val |= (CNTHCTL_EL1PCTEN << cnthctl_shift); 1300 + if (cpus_have_final_cap(ARM64_HAS_ECV_CNTPOFF)) 1301 + val |= CNTHCTL_ECV; 1314 1302 write_sysreg(val, cnthctl_el2); 1315 1303 } 1316 1304
+1 -1
arch/arm64/kvm/hypercalls.c
··· 47 47 cycles = systime_snapshot.cycles - vcpu->kvm->arch.timer_data.voffset; 48 48 break; 49 49 case KVM_PTP_PHYS_COUNTER: 50 - cycles = systime_snapshot.cycles; 50 + cycles = systime_snapshot.cycles - vcpu->kvm->arch.timer_data.poffset; 51 51 break; 52 52 default: 53 53 return;
+1
include/clocksource/arm_arch_timer.h
··· 21 21 #define CNTHCTL_EVNTEN (1 << 2) 22 22 #define CNTHCTL_EVNTDIR (1 << 3) 23 23 #define CNTHCTL_EVNTI (0xF << 4) 24 + #define CNTHCTL_ECV (1 << 12) 24 25 25 26 enum arch_timer_reg { 26 27 ARCH_TIMER_REG_CTRL,
+2
include/kvm/arm_arch_timer.h
··· 34 34 struct arch_timer_vm_data { 35 35 /* Offset applied to the virtual timer/counter */ 36 36 u64 voffset; 37 + /* Offset applied to the physical timer/counter */ 38 + u64 poffset; 37 39 }; 38 40 39 41 struct arch_timer_context {