1*a8cf291bSJianyong Wu // SPDX-License-Identifier: GPL-2.0-or-later 2*a8cf291bSJianyong Wu /* 3*a8cf291bSJianyong Wu * Virtual PTP 1588 clock for use with KVM guests 4*a8cf291bSJianyong Wu * 5*a8cf291bSJianyong Wu * Copyright (C) 2017 Red Hat Inc. 6*a8cf291bSJianyong Wu */ 7*a8cf291bSJianyong Wu 8*a8cf291bSJianyong Wu #include <linux/device.h> 9*a8cf291bSJianyong Wu #include <linux/kernel.h> 10*a8cf291bSJianyong Wu #include <asm/pvclock.h> 11*a8cf291bSJianyong Wu #include <asm/kvmclock.h> 12*a8cf291bSJianyong Wu #include <linux/module.h> 13*a8cf291bSJianyong Wu #include <uapi/asm/kvm_para.h> 14*a8cf291bSJianyong Wu #include <uapi/linux/kvm_para.h> 15*a8cf291bSJianyong Wu #include <linux/ptp_clock_kernel.h> 16*a8cf291bSJianyong Wu #include <linux/ptp_kvm.h> 17*a8cf291bSJianyong Wu 18*a8cf291bSJianyong Wu struct pvclock_vsyscall_time_info *hv_clock; 19*a8cf291bSJianyong Wu 20*a8cf291bSJianyong Wu static phys_addr_t clock_pair_gpa; 21*a8cf291bSJianyong Wu static struct kvm_clock_pairing clock_pair; 22*a8cf291bSJianyong Wu 23*a8cf291bSJianyong Wu int kvm_arch_ptp_init(void) 24*a8cf291bSJianyong Wu { 25*a8cf291bSJianyong Wu long ret; 26*a8cf291bSJianyong Wu 27*a8cf291bSJianyong Wu if (!kvm_para_available()) 28*a8cf291bSJianyong Wu return -ENODEV; 29*a8cf291bSJianyong Wu 30*a8cf291bSJianyong Wu clock_pair_gpa = slow_virt_to_phys(&clock_pair); 31*a8cf291bSJianyong Wu hv_clock = pvclock_get_pvti_cpu0_va(); 32*a8cf291bSJianyong Wu if (!hv_clock) 33*a8cf291bSJianyong Wu return -ENODEV; 34*a8cf291bSJianyong Wu 35*a8cf291bSJianyong Wu ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa, 36*a8cf291bSJianyong Wu KVM_CLOCK_PAIRING_WALLCLOCK); 37*a8cf291bSJianyong Wu if (ret == -KVM_ENOSYS || ret == -KVM_EOPNOTSUPP) 38*a8cf291bSJianyong Wu return -ENODEV; 39*a8cf291bSJianyong Wu 40*a8cf291bSJianyong Wu return 0; 41*a8cf291bSJianyong Wu } 42*a8cf291bSJianyong Wu 43*a8cf291bSJianyong Wu int kvm_arch_ptp_get_clock(struct timespec64 *ts) 44*a8cf291bSJianyong Wu { 45*a8cf291bSJianyong Wu long ret; 46*a8cf291bSJianyong Wu 47*a8cf291bSJianyong Wu ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, 48*a8cf291bSJianyong Wu clock_pair_gpa, 49*a8cf291bSJianyong Wu KVM_CLOCK_PAIRING_WALLCLOCK); 50*a8cf291bSJianyong Wu if (ret != 0) { 51*a8cf291bSJianyong Wu pr_err_ratelimited("clock offset hypercall ret %lu\n", ret); 52*a8cf291bSJianyong Wu return -EOPNOTSUPP; 53*a8cf291bSJianyong Wu } 54*a8cf291bSJianyong Wu 55*a8cf291bSJianyong Wu ts->tv_sec = clock_pair.sec; 56*a8cf291bSJianyong Wu ts->tv_nsec = clock_pair.nsec; 57*a8cf291bSJianyong Wu 58*a8cf291bSJianyong Wu return 0; 59*a8cf291bSJianyong Wu } 60*a8cf291bSJianyong Wu 61*a8cf291bSJianyong Wu int kvm_arch_ptp_get_crosststamp(u64 *cycle, struct timespec64 *tspec, 62*a8cf291bSJianyong Wu struct clocksource **cs) 63*a8cf291bSJianyong Wu { 64*a8cf291bSJianyong Wu struct pvclock_vcpu_time_info *src; 65*a8cf291bSJianyong Wu unsigned int version; 66*a8cf291bSJianyong Wu long ret; 67*a8cf291bSJianyong Wu int cpu; 68*a8cf291bSJianyong Wu 69*a8cf291bSJianyong Wu cpu = smp_processor_id(); 70*a8cf291bSJianyong Wu src = &hv_clock[cpu].pvti; 71*a8cf291bSJianyong Wu 72*a8cf291bSJianyong Wu do { 73*a8cf291bSJianyong Wu /* 74*a8cf291bSJianyong Wu * We are using a TSC value read in the hosts 75*a8cf291bSJianyong Wu * kvm_hc_clock_pairing handling. 76*a8cf291bSJianyong Wu * So any changes to tsc_to_system_mul 77*a8cf291bSJianyong Wu * and tsc_shift or any other pvclock 78*a8cf291bSJianyong Wu * data invalidate that measurement. 79*a8cf291bSJianyong Wu */ 80*a8cf291bSJianyong Wu version = pvclock_read_begin(src); 81*a8cf291bSJianyong Wu 82*a8cf291bSJianyong Wu ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, 83*a8cf291bSJianyong Wu clock_pair_gpa, 84*a8cf291bSJianyong Wu KVM_CLOCK_PAIRING_WALLCLOCK); 85*a8cf291bSJianyong Wu if (ret != 0) { 86*a8cf291bSJianyong Wu pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret); 87*a8cf291bSJianyong Wu return -EOPNOTSUPP; 88*a8cf291bSJianyong Wu } 89*a8cf291bSJianyong Wu tspec->tv_sec = clock_pair.sec; 90*a8cf291bSJianyong Wu tspec->tv_nsec = clock_pair.nsec; 91*a8cf291bSJianyong Wu *cycle = __pvclock_read_cycles(src, clock_pair.tsc); 92*a8cf291bSJianyong Wu } while (pvclock_read_retry(src, version)); 93*a8cf291bSJianyong Wu 94*a8cf291bSJianyong Wu *cs = &kvm_clock; 95*a8cf291bSJianyong Wu 96*a8cf291bSJianyong Wu return 0; 97*a8cf291bSJianyong Wu } 98