1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Virtual PTP 1588 clock for use with KVM guests
5 * Copyright (C) 2017 Red Hat Inc.
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>
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
;
23 int kvm_arch_ptp_init(void)
28 if (!kvm_para_available())
31 if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT
)) {
32 p
= alloc_page(GFP_KERNEL
| __GFP_ZERO
);
36 clock_pair
= page_address(p
);
37 ret
= set_memory_decrypted((unsigned long)clock_pair
, 1);
44 clock_pair
= &clock_pair_glbl
;
47 clock_pair_gpa
= slow_virt_to_phys(clock_pair
);
48 if (!pvclock_get_pvti_cpu0_va()) {
53 ret
= kvm_hypercall2(KVM_HC_CLOCK_PAIRING
, clock_pair_gpa
,
54 KVM_CLOCK_PAIRING_WALLCLOCK
);
55 if (ret
== -KVM_ENOSYS
) {
68 void kvm_arch_ptp_exit(void)
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
);
77 int kvm_arch_ptp_get_clock(struct timespec64
*ts
)
81 ret
= kvm_hypercall2(KVM_HC_CLOCK_PAIRING
,
83 KVM_CLOCK_PAIRING_WALLCLOCK
);
85 pr_err_ratelimited("clock offset hypercall ret %lu\n", ret
);
89 ts
->tv_sec
= clock_pair
->sec
;
90 ts
->tv_nsec
= clock_pair
->nsec
;
95 int kvm_arch_ptp_get_crosststamp(u64
*cycle
, struct timespec64
*tspec
,
96 enum clocksource_ids
*cs_id
)
98 struct pvclock_vcpu_time_info
*src
;
102 src
= this_cpu_pvti();
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.
112 version
= pvclock_read_begin(src
);
114 ret
= kvm_hypercall2(KVM_HC_CLOCK_PAIRING
,
116 KVM_CLOCK_PAIRING_WALLCLOCK
);
118 pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret
);
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
));
126 *cs_id
= CSID_X86_KVM_CLK
;