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 #include <linux/device.h> 8 #include <linux/err.h> 9 #include <linux/init.h> 10 #include <linux/kernel.h> 11 #include <linux/slab.h> 12 #include <linux/module.h> 13 #include <linux/ptp_kvm.h> 14 #include <uapi/linux/kvm_para.h> 15 #include <asm/kvm_para.h> 16 #include <uapi/asm/kvm_para.h> 17 18 #include <linux/ptp_clock_kernel.h> 19 20 struct kvm_ptp_clock { 21 struct ptp_clock *ptp_clock; 22 struct ptp_clock_info caps; 23 }; 24 25 static DEFINE_SPINLOCK(kvm_ptp_lock); 26 27 static int ptp_kvm_get_time_fn(ktime_t *device_time, 28 struct system_counterval_t *system_counter, 29 void *ctx) 30 { 31 long ret; 32 u64 cycle; 33 struct timespec64 tspec; 34 struct clocksource *cs; 35 36 spin_lock(&kvm_ptp_lock); 37 38 preempt_disable_notrace(); 39 ret = kvm_arch_ptp_get_crosststamp(&cycle, &tspec, &cs); 40 if (ret) { 41 spin_unlock(&kvm_ptp_lock); 42 preempt_enable_notrace(); 43 return ret; 44 } 45 46 preempt_enable_notrace(); 47 48 system_counter->cycles = cycle; 49 system_counter->cs = cs; 50 51 *device_time = timespec64_to_ktime(tspec); 52 53 spin_unlock(&kvm_ptp_lock); 54 55 return 0; 56 } 57 58 static int ptp_kvm_getcrosststamp(struct ptp_clock_info *ptp, 59 struct system_device_crosststamp *xtstamp) 60 { 61 return get_device_system_crosststamp(ptp_kvm_get_time_fn, NULL, 62 NULL, xtstamp); 63 } 64 65 /* 66 * PTP clock operations 67 */ 68 69 static int ptp_kvm_adjfreq(struct ptp_clock_info *ptp, s32 ppb) 70 { 71 return -EOPNOTSUPP; 72 } 73 74 static int ptp_kvm_adjtime(struct ptp_clock_info *ptp, s64 delta) 75 { 76 return -EOPNOTSUPP; 77 } 78 79 static int ptp_kvm_settime(struct ptp_clock_info *ptp, 80 const struct timespec64 *ts) 81 { 82 return -EOPNOTSUPP; 83 } 84 85 static int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 86 { 87 long ret; 88 struct timespec64 tspec; 89 90 spin_lock(&kvm_ptp_lock); 91 92 ret = kvm_arch_ptp_get_clock(&tspec); 93 if (ret) { 94 spin_unlock(&kvm_ptp_lock); 95 return ret; 96 } 97 98 spin_unlock(&kvm_ptp_lock); 99 100 memcpy(ts, &tspec, sizeof(struct timespec64)); 101 102 return 0; 103 } 104 105 static int ptp_kvm_enable(struct ptp_clock_info *ptp, 106 struct ptp_clock_request *rq, int on) 107 { 108 return -EOPNOTSUPP; 109 } 110 111 static const struct ptp_clock_info ptp_kvm_caps = { 112 .owner = THIS_MODULE, 113 .name = "KVM virtual PTP", 114 .max_adj = 0, 115 .n_ext_ts = 0, 116 .n_pins = 0, 117 .pps = 0, 118 .adjfreq = ptp_kvm_adjfreq, 119 .adjtime = ptp_kvm_adjtime, 120 .gettime64 = ptp_kvm_gettime, 121 .settime64 = ptp_kvm_settime, 122 .enable = ptp_kvm_enable, 123 .getcrosststamp = ptp_kvm_getcrosststamp, 124 }; 125 126 /* module operations */ 127 128 static struct kvm_ptp_clock kvm_ptp_clock; 129 130 static void __exit ptp_kvm_exit(void) 131 { 132 ptp_clock_unregister(kvm_ptp_clock.ptp_clock); 133 } 134 135 static int __init ptp_kvm_init(void) 136 { 137 long ret; 138 139 ret = kvm_arch_ptp_init(); 140 if (ret) { 141 if (ret != -EOPNOTSUPP) 142 pr_err("fail to initialize ptp_kvm"); 143 return ret; 144 } 145 146 kvm_ptp_clock.caps = ptp_kvm_caps; 147 148 kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL); 149 150 return PTR_ERR_OR_ZERO(kvm_ptp_clock.ptp_clock); 151 } 152 153 module_init(ptp_kvm_init); 154 module_exit(ptp_kvm_exit); 155 156 MODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>"); 157 MODULE_DESCRIPTION("PTP clock using KVMCLOCK"); 158 MODULE_LICENSE("GPL"); 159