Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 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 | |
| 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> |
Jeremi Piotrowski | 6365ba6 | 2023-03-08 15:05:31 +0000 | [diff] [blame] | 17 | #include <linux/set_memory.h> |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 18 | |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 19 | static phys_addr_t clock_pair_gpa; |
Jeremi Piotrowski | 6365ba6 | 2023-03-08 15:05:31 +0000 | [diff] [blame] | 20 | static struct kvm_clock_pairing clock_pair_glbl; |
| 21 | static struct kvm_clock_pairing *clock_pair; |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 22 | |
| 23 | int kvm_arch_ptp_init(void) |
| 24 | { |
Jeremi Piotrowski | 6365ba6 | 2023-03-08 15:05:31 +0000 | [diff] [blame] | 25 | struct page *p; |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 26 | long ret; |
| 27 | |
| 28 | if (!kvm_para_available()) |
| 29 | return -ENODEV; |
| 30 | |
Jeremi Piotrowski | 6365ba6 | 2023-03-08 15:05:31 +0000 | [diff] [blame] | 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 | } |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 52 | |
| 53 | ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa, |
| 54 | KVM_CLOCK_PAIRING_WALLCLOCK); |
Jeremi Piotrowski | 6365ba6 | 2023-03-08 15:05:31 +0000 | [diff] [blame] | 55 | if (ret == -KVM_ENOSYS) { |
| 56 | ret = -ENODEV; |
| 57 | goto err; |
| 58 | } |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 59 | |
Kele Huang | c2402d4 | 2021-10-14 11:19:52 +0800 | [diff] [blame] | 60 | return ret; |
Jeremi Piotrowski | 6365ba6 | 2023-03-08 15:05:31 +0000 | [diff] [blame] | 61 | |
| 62 | err: |
| 63 | kvm_arch_ptp_exit(); |
| 64 | nofree: |
| 65 | return ret; |
| 66 | } |
| 67 | |
| 68 | void 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 | } |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 75 | } |
| 76 | |
| 77 | int 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 | |
Jeremi Piotrowski | 6365ba6 | 2023-03-08 15:05:31 +0000 | [diff] [blame] | 89 | ts->tv_sec = clock_pair->sec; |
| 90 | ts->tv_nsec = clock_pair->nsec; |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 91 | |
| 92 | return 0; |
| 93 | } |
| 94 | |
| 95 | int kvm_arch_ptp_get_crosststamp(u64 *cycle, struct timespec64 *tspec, |
Peter Hilber | 9be3b2f | 2024-02-01 02:04:50 +0100 | [diff] [blame] | 96 | enum clocksource_ids *cs_id) |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 97 | { |
| 98 | struct pvclock_vcpu_time_info *src; |
| 99 | unsigned int version; |
| 100 | long ret; |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 101 | |
Zelin Deng | 773e89a | 2021-09-29 13:13:49 +0800 | [diff] [blame] | 102 | src = this_cpu_pvti(); |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 103 | |
| 104 | do { |
| 105 | /* |
| 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. |
| 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 | } |
Jeremi Piotrowski | 6365ba6 | 2023-03-08 15:05:31 +0000 | [diff] [blame] | 121 | tspec->tv_sec = clock_pair->sec; |
| 122 | tspec->tv_nsec = clock_pair->nsec; |
| 123 | *cycle = __pvclock_read_cycles(src, clock_pair->tsc); |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 124 | } while (pvclock_read_retry(src, version)); |
| 125 | |
Peter Hilber | 9be3b2f | 2024-02-01 02:04:50 +0100 | [diff] [blame] | 126 | *cs_id = CSID_X86_KVM_CLK; |
Jianyong Wu | a8cf291 | 2020-12-09 14:09:26 +0800 | [diff] [blame] | 127 | |
| 128 | return 0; |
| 129 | } |