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 #include <linux/set_memory.h> 18 19 static phys_addr_t clock_pair_gpa; 20 static struct kvm_clock_pairing clock_pair_glbl; 21 static struct kvm_clock_pairing *clock_pair; 22 23 int kvm_arch_ptp_init(void) 24 { 25 struct page *p; 26 long ret; 27 28 if (!kvm_para_available()) 29 return -ENODEV; 30 31 if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) { 32 p = alloc_page(GFP_KERNEL | __GFP_ZERO); 33 if (!p) 34 return -ENOMEM; 35 36 clock_pair = page_address(p); 37 ret = set_memory_decrypted((unsigned long)clock_pair, 1); 38 if (ret) { 39 __free_page(p); 40 clock_pair = NULL; 41 goto nofree; 42 } 43 } else { 44 clock_pair = &clock_pair_glbl; 45 } 46 47 clock_pair_gpa = slow_virt_to_phys(clock_pair); 48 if (!pvclock_get_pvti_cpu0_va()) { 49 ret = -ENODEV; 50 goto err; 51 } 52 53 ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa, 54 KVM_CLOCK_PAIRING_WALLCLOCK); 55 if (ret == -KVM_ENOSYS) { 56 ret = -ENODEV; 57 goto err; 58 } 59 60 return ret; 61 62 err: 63 kvm_arch_ptp_exit(); 64 nofree: 65 return ret; 66 } 67 68 void kvm_arch_ptp_exit(void) 69 { 70 if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) { 71 WARN_ON(set_memory_encrypted((unsigned long)clock_pair, 1)); 72 free_page((unsigned long)clock_pair); 73 clock_pair = NULL; 74 } 75 } 76 77 int kvm_arch_ptp_get_clock(struct timespec64 *ts) 78 { 79 long ret; 80 81 ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, 82 clock_pair_gpa, 83 KVM_CLOCK_PAIRING_WALLCLOCK); 84 if (ret != 0) { 85 pr_err_ratelimited("clock offset hypercall ret %lu\n", ret); 86 return -EOPNOTSUPP; 87 } 88 89 ts->tv_sec = clock_pair->sec; 90 ts->tv_nsec = clock_pair->nsec; 91 92 return 0; 93 } 94 95 int kvm_arch_ptp_get_crosststamp(u64 *cycle, struct timespec64 *tspec, 96 enum clocksource_ids *cs_id) 97 { 98 struct pvclock_vcpu_time_info *src; 99 unsigned int version; 100 long ret; 101 102 src = this_cpu_pvti(); 103 104 do { 105 /* 106 * We are using a TSC value read in the hosts 107 * kvm_hc_clock_pairing handling. 108 * So any changes to tsc_to_system_mul 109 * and tsc_shift or any other pvclock 110 * data invalidate that measurement. 111 */ 112 version = pvclock_read_begin(src); 113 114 ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, 115 clock_pair_gpa, 116 KVM_CLOCK_PAIRING_WALLCLOCK); 117 if (ret != 0) { 118 pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret); 119 return -EOPNOTSUPP; 120 } 121 tspec->tv_sec = clock_pair->sec; 122 tspec->tv_nsec = clock_pair->nsec; 123 *cycle = __pvclock_read_cycles(src, clock_pair->tsc); 124 } while (pvclock_read_retry(src, version)); 125 126 *cs_id = CSID_X86_KVM_CLK; 127 128 return 0; 129 } 130