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