xref: /kernel/linux/linux-5.10/drivers/ptp/ptp_kvm.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Virtual PTP 1588 clock for use with KVM guests
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Red Hat Inc.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/device.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <uapi/linux/kvm_para.h>
138c2ecf20Sopenharmony_ci#include <asm/kvm_para.h>
148c2ecf20Sopenharmony_ci#include <asm/pvclock.h>
158c2ecf20Sopenharmony_ci#include <asm/kvmclock.h>
168c2ecf20Sopenharmony_ci#include <uapi/asm/kvm_para.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/ptp_clock_kernel.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct kvm_ptp_clock {
218c2ecf20Sopenharmony_ci	struct ptp_clock *ptp_clock;
228c2ecf20Sopenharmony_ci	struct ptp_clock_info caps;
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(kvm_ptp_lock);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct pvclock_vsyscall_time_info *hv_clock;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic struct kvm_clock_pairing clock_pair;
308c2ecf20Sopenharmony_cistatic phys_addr_t clock_pair_gpa;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int ptp_kvm_get_time_fn(ktime_t *device_time,
338c2ecf20Sopenharmony_ci			       struct system_counterval_t *system_counter,
348c2ecf20Sopenharmony_ci			       void *ctx)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	unsigned long ret;
378c2ecf20Sopenharmony_ci	struct timespec64 tspec;
388c2ecf20Sopenharmony_ci	unsigned version;
398c2ecf20Sopenharmony_ci	int cpu;
408c2ecf20Sopenharmony_ci	struct pvclock_vcpu_time_info *src;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	spin_lock(&kvm_ptp_lock);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	preempt_disable_notrace();
458c2ecf20Sopenharmony_ci	cpu = smp_processor_id();
468c2ecf20Sopenharmony_ci	src = &hv_clock[cpu].pvti;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	do {
498c2ecf20Sopenharmony_ci		/*
508c2ecf20Sopenharmony_ci		 * We are using a TSC value read in the hosts
518c2ecf20Sopenharmony_ci		 * kvm_hc_clock_pairing handling.
528c2ecf20Sopenharmony_ci		 * So any changes to tsc_to_system_mul
538c2ecf20Sopenharmony_ci		 * and tsc_shift or any other pvclock
548c2ecf20Sopenharmony_ci		 * data invalidate that measurement.
558c2ecf20Sopenharmony_ci		 */
568c2ecf20Sopenharmony_ci		version = pvclock_read_begin(src);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING,
598c2ecf20Sopenharmony_ci				     clock_pair_gpa,
608c2ecf20Sopenharmony_ci				     KVM_CLOCK_PAIRING_WALLCLOCK);
618c2ecf20Sopenharmony_ci		if (ret != 0) {
628c2ecf20Sopenharmony_ci			pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret);
638c2ecf20Sopenharmony_ci			spin_unlock(&kvm_ptp_lock);
648c2ecf20Sopenharmony_ci			preempt_enable_notrace();
658c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
668c2ecf20Sopenharmony_ci		}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		tspec.tv_sec = clock_pair.sec;
698c2ecf20Sopenharmony_ci		tspec.tv_nsec = clock_pair.nsec;
708c2ecf20Sopenharmony_ci		ret = __pvclock_read_cycles(src, clock_pair.tsc);
718c2ecf20Sopenharmony_ci	} while (pvclock_read_retry(src, version));
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	preempt_enable_notrace();
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	system_counter->cycles = ret;
768c2ecf20Sopenharmony_ci	system_counter->cs = &kvm_clock;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	*device_time = timespec64_to_ktime(tspec);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	spin_unlock(&kvm_ptp_lock);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int ptp_kvm_getcrosststamp(struct ptp_clock_info *ptp,
868c2ecf20Sopenharmony_ci				  struct system_device_crosststamp *xtstamp)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	return get_device_system_crosststamp(ptp_kvm_get_time_fn, NULL,
898c2ecf20Sopenharmony_ci					     NULL, xtstamp);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/*
938c2ecf20Sopenharmony_ci * PTP clock operations
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int ptp_kvm_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int ptp_kvm_adjtime(struct ptp_clock_info *ptp, s64 delta)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int ptp_kvm_settime(struct ptp_clock_info *ptp,
1078c2ecf20Sopenharmony_ci			   const struct timespec64 *ts)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	unsigned long ret;
1158c2ecf20Sopenharmony_ci	struct timespec64 tspec;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	spin_lock(&kvm_ptp_lock);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING,
1208c2ecf20Sopenharmony_ci			     clock_pair_gpa,
1218c2ecf20Sopenharmony_ci			     KVM_CLOCK_PAIRING_WALLCLOCK);
1228c2ecf20Sopenharmony_ci	if (ret != 0) {
1238c2ecf20Sopenharmony_ci		pr_err_ratelimited("clock offset hypercall ret %lu\n", ret);
1248c2ecf20Sopenharmony_ci		spin_unlock(&kvm_ptp_lock);
1258c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	tspec.tv_sec = clock_pair.sec;
1298c2ecf20Sopenharmony_ci	tspec.tv_nsec = clock_pair.nsec;
1308c2ecf20Sopenharmony_ci	spin_unlock(&kvm_ptp_lock);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	memcpy(ts, &tspec, sizeof(struct timespec64));
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int ptp_kvm_enable(struct ptp_clock_info *ptp,
1388c2ecf20Sopenharmony_ci			  struct ptp_clock_request *rq, int on)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic const struct ptp_clock_info ptp_kvm_caps = {
1448c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
1458c2ecf20Sopenharmony_ci	.name		= "KVM virtual PTP",
1468c2ecf20Sopenharmony_ci	.max_adj	= 0,
1478c2ecf20Sopenharmony_ci	.n_ext_ts	= 0,
1488c2ecf20Sopenharmony_ci	.n_pins		= 0,
1498c2ecf20Sopenharmony_ci	.pps		= 0,
1508c2ecf20Sopenharmony_ci	.adjfreq	= ptp_kvm_adjfreq,
1518c2ecf20Sopenharmony_ci	.adjtime	= ptp_kvm_adjtime,
1528c2ecf20Sopenharmony_ci	.gettime64	= ptp_kvm_gettime,
1538c2ecf20Sopenharmony_ci	.settime64	= ptp_kvm_settime,
1548c2ecf20Sopenharmony_ci	.enable		= ptp_kvm_enable,
1558c2ecf20Sopenharmony_ci	.getcrosststamp = ptp_kvm_getcrosststamp,
1568c2ecf20Sopenharmony_ci};
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/* module operations */
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic struct kvm_ptp_clock kvm_ptp_clock;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void __exit ptp_kvm_exit(void)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	ptp_clock_unregister(kvm_ptp_clock.ptp_clock);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int __init ptp_kvm_init(void)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	long ret;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (!kvm_para_available())
1728c2ecf20Sopenharmony_ci		return -ENODEV;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	clock_pair_gpa = slow_virt_to_phys(&clock_pair);
1758c2ecf20Sopenharmony_ci	hv_clock = pvclock_get_pvti_cpu0_va();
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (!hv_clock)
1788c2ecf20Sopenharmony_ci		return -ENODEV;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa,
1818c2ecf20Sopenharmony_ci			KVM_CLOCK_PAIRING_WALLCLOCK);
1828c2ecf20Sopenharmony_ci	if (ret == -KVM_ENOSYS || ret == -KVM_EOPNOTSUPP)
1838c2ecf20Sopenharmony_ci		return -ENODEV;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	kvm_ptp_clock.caps = ptp_kvm_caps;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	kvm_ptp_clock.ptp_clock = ptp_clock_register(&kvm_ptp_clock.caps, NULL);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(kvm_ptp_clock.ptp_clock);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cimodule_init(ptp_kvm_init);
1938c2ecf20Sopenharmony_cimodule_exit(ptp_kvm_exit);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marcelo Tosatti <mtosatti@redhat.com>");
1968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PTP clock using KVMCLOCK");
1978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
198