162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Virtual PTP 1588 clock for use with KVM guests 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Red Hat Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/device.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <asm/pvclock.h> 1162306a36Sopenharmony_ci#include <asm/kvmclock.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <uapi/asm/kvm_para.h> 1462306a36Sopenharmony_ci#include <uapi/linux/kvm_para.h> 1562306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 1662306a36Sopenharmony_ci#include <linux/ptp_kvm.h> 1762306a36Sopenharmony_ci#include <linux/set_memory.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic phys_addr_t clock_pair_gpa; 2062306a36Sopenharmony_cistatic struct kvm_clock_pairing clock_pair_glbl; 2162306a36Sopenharmony_cistatic struct kvm_clock_pairing *clock_pair; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciint kvm_arch_ptp_init(void) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct page *p; 2662306a36Sopenharmony_ci long ret; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (!kvm_para_available()) 2962306a36Sopenharmony_ci return -ENODEV; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) { 3262306a36Sopenharmony_ci p = alloc_page(GFP_KERNEL | __GFP_ZERO); 3362306a36Sopenharmony_ci if (!p) 3462306a36Sopenharmony_ci return -ENOMEM; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci clock_pair = page_address(p); 3762306a36Sopenharmony_ci ret = set_memory_decrypted((unsigned long)clock_pair, 1); 3862306a36Sopenharmony_ci if (ret) { 3962306a36Sopenharmony_ci __free_page(p); 4062306a36Sopenharmony_ci clock_pair = NULL; 4162306a36Sopenharmony_ci goto nofree; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci } else { 4462306a36Sopenharmony_ci clock_pair = &clock_pair_glbl; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci clock_pair_gpa = slow_virt_to_phys(clock_pair); 4862306a36Sopenharmony_ci if (!pvclock_get_pvti_cpu0_va()) { 4962306a36Sopenharmony_ci ret = -ENODEV; 5062306a36Sopenharmony_ci goto err; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa, 5462306a36Sopenharmony_ci KVM_CLOCK_PAIRING_WALLCLOCK); 5562306a36Sopenharmony_ci if (ret == -KVM_ENOSYS) { 5662306a36Sopenharmony_ci ret = -ENODEV; 5762306a36Sopenharmony_ci goto err; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return ret; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cierr: 6362306a36Sopenharmony_ci kvm_arch_ptp_exit(); 6462306a36Sopenharmony_cinofree: 6562306a36Sopenharmony_ci return ret; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_civoid kvm_arch_ptp_exit(void) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) { 7162306a36Sopenharmony_ci WARN_ON(set_memory_encrypted((unsigned long)clock_pair, 1)); 7262306a36Sopenharmony_ci free_page((unsigned long)clock_pair); 7362306a36Sopenharmony_ci clock_pair = NULL; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciint kvm_arch_ptp_get_clock(struct timespec64 *ts) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci long ret; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, 8262306a36Sopenharmony_ci clock_pair_gpa, 8362306a36Sopenharmony_ci KVM_CLOCK_PAIRING_WALLCLOCK); 8462306a36Sopenharmony_ci if (ret != 0) { 8562306a36Sopenharmony_ci pr_err_ratelimited("clock offset hypercall ret %lu\n", ret); 8662306a36Sopenharmony_ci return -EOPNOTSUPP; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci ts->tv_sec = clock_pair->sec; 9062306a36Sopenharmony_ci ts->tv_nsec = clock_pair->nsec; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ciint kvm_arch_ptp_get_crosststamp(u64 *cycle, struct timespec64 *tspec, 9662306a36Sopenharmony_ci struct clocksource **cs) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct pvclock_vcpu_time_info *src; 9962306a36Sopenharmony_ci unsigned int version; 10062306a36Sopenharmony_ci long ret; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci src = this_cpu_pvti(); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci do { 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * We are using a TSC value read in the hosts 10762306a36Sopenharmony_ci * kvm_hc_clock_pairing handling. 10862306a36Sopenharmony_ci * So any changes to tsc_to_system_mul 10962306a36Sopenharmony_ci * and tsc_shift or any other pvclock 11062306a36Sopenharmony_ci * data invalidate that measurement. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci version = pvclock_read_begin(src); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, 11562306a36Sopenharmony_ci clock_pair_gpa, 11662306a36Sopenharmony_ci KVM_CLOCK_PAIRING_WALLCLOCK); 11762306a36Sopenharmony_ci if (ret != 0) { 11862306a36Sopenharmony_ci pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret); 11962306a36Sopenharmony_ci return -EOPNOTSUPP; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci tspec->tv_sec = clock_pair->sec; 12262306a36Sopenharmony_ci tspec->tv_nsec = clock_pair->nsec; 12362306a36Sopenharmony_ci *cycle = __pvclock_read_cycles(src, clock_pair->tsc); 12462306a36Sopenharmony_ci } while (pvclock_read_retry(src, version)); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci *cs = &kvm_clock; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 130