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