130 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Virtual PTP 1588 clock for use with KVM guests4 *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 19static phys_addr_t clock_pair_gpa;20static struct kvm_clock_pairing clock_pair_glbl;21static struct kvm_clock_pairing *clock_pair;22 23int 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 62err:63 kvm_arch_ptp_exit();64nofree:65 return ret;66}67 68void 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 77int 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 95int 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 hosts107 * kvm_hc_clock_pairing handling.108 * So any changes to tsc_to_system_mul109 * and tsc_shift or any other pvclock110 * 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