Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: arm64: Add support for the KVM PTP service

Implement the hypervisor side of the KVM PTP interface.

The service offers wall time and cycle count from host to guest.
The caller must specify whether they want the host's view of
either the virtual or physical counter.

Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20201209060932.212364-7-jianyong.wu@arm.com

authored by

Jianyong Wu and committed by
Marc Zyngier
3bf72569 100148d0

+107
+10
Documentation/virt/kvm/api.rst
··· 6724 6724 The KVM_XEN_HVM_CONFIG_RUNSTATE flag indicates that the runstate-related 6725 6725 features KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR/_CURRENT/_DATA/_ADJUST are 6726 6726 supported by the KVM_XEN_VCPU_SET_ATTR/KVM_XEN_VCPU_GET_ATTR ioctls. 6727 + 6728 + 8.31 KVM_CAP_PTP_KVM 6729 + -------------------- 6730 + 6731 + :Architectures: arm64 6732 + 6733 + This capability indicates that the KVM virtual PTP service is 6734 + supported in the host. A VMM can check whether the service is 6735 + available to the guest on migration. 6736 +
+1
Documentation/virt/kvm/arm/index.rst
··· 10 10 hyp-abi 11 11 psci 12 12 pvtime 13 + ptp_kvm
+25
Documentation/virt/kvm/arm/ptp_kvm.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + PTP_KVM support for arm/arm64 4 + ============================= 5 + 6 + PTP_KVM is used for high precision time sync between host and guests. 7 + It relies on transferring the wall clock and counter value from the 8 + host to the guest using a KVM-specific hypercall. 9 + 10 + * ARM_SMCCC_HYP_KVM_PTP_FUNC_ID: 0x86000001 11 + 12 + This hypercall uses the SMC32/HVC32 calling convention: 13 + 14 + ARM_SMCCC_HYP_KVM_PTP_FUNC_ID 15 + ============= ========== ========== 16 + Function ID: (uint32) 0x86000001 17 + Arguments: (uint32) KVM_PTP_VIRT_COUNTER(0) 18 + KVM_PTP_PHYS_COUNTER(1) 19 + Return Values: (int32) NOT_SUPPORTED(-1) on error, or 20 + (uint32) Upper 32 bits of wall clock time (r0) 21 + (uint32) Lower 32 bits of wall clock time (r1) 22 + (uint32) Upper 32 bits of counter (r2) 23 + (uint32) Lower 32 bits of counter (r3) 24 + Endianness: No Restrictions. 25 + ============= ========== ==========
+1
arch/arm64/kvm/arm.c
··· 206 206 case KVM_CAP_ARM_INJECT_EXT_DABT: 207 207 case KVM_CAP_SET_GUEST_DEBUG: 208 208 case KVM_CAP_VCPU_ATTRIBUTES: 209 + case KVM_CAP_PTP_KVM: 209 210 r = 1; 210 211 break; 211 212 case KVM_CAP_ARM_SET_DEVICE_ADDR:
+53
arch/arm64/kvm/hypercalls.c
··· 9 9 #include <kvm/arm_hypercalls.h> 10 10 #include <kvm/arm_psci.h> 11 11 12 + static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val) 13 + { 14 + struct system_time_snapshot systime_snapshot; 15 + u64 cycles = ~0UL; 16 + u32 feature; 17 + 18 + /* 19 + * system time and counter value must captured at the same 20 + * time to keep consistency and precision. 21 + */ 22 + ktime_get_snapshot(&systime_snapshot); 23 + 24 + /* 25 + * This is only valid if the current clocksource is the 26 + * architected counter, as this is the only one the guest 27 + * can see. 28 + */ 29 + if (systime_snapshot.cs_id != CSID_ARM_ARCH_COUNTER) 30 + return; 31 + 32 + /* 33 + * The guest selects one of the two reference counters 34 + * (virtual or physical) with the first argument of the SMCCC 35 + * call. In case the identifier is not supported, error out. 36 + */ 37 + feature = smccc_get_arg1(vcpu); 38 + switch (feature) { 39 + case KVM_PTP_VIRT_COUNTER: 40 + cycles = systime_snapshot.cycles - vcpu_read_sys_reg(vcpu, CNTVOFF_EL2); 41 + break; 42 + case KVM_PTP_PHYS_COUNTER: 43 + cycles = systime_snapshot.cycles; 44 + break; 45 + default: 46 + return; 47 + } 48 + 49 + /* 50 + * This relies on the top bit of val[0] never being set for 51 + * valid values of system time, because that is *really* far 52 + * in the future (about 292 years from 1970, and at that stage 53 + * nobody will give a damn about it). 54 + */ 55 + val[0] = upper_32_bits(systime_snapshot.real); 56 + val[1] = lower_32_bits(systime_snapshot.real); 57 + val[2] = upper_32_bits(cycles); 58 + val[3] = lower_32_bits(cycles); 59 + } 60 + 12 61 int kvm_hvc_call_handler(struct kvm_vcpu *vcpu) 13 62 { 14 63 u32 func_id = smccc_get_function(vcpu); ··· 128 79 break; 129 80 case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID: 130 81 val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES); 82 + val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP); 83 + break; 84 + case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID: 85 + kvm_ptp_get_time(vcpu, val); 131 86 break; 132 87 case ARM_SMCCC_TRNG_VERSION: 133 88 case ARM_SMCCC_TRNG_FEATURES:
+16
include/linux/arm-smccc.h
··· 103 103 104 104 /* KVM "vendor specific" services */ 105 105 #define ARM_SMCCC_KVM_FUNC_FEATURES 0 106 + #define ARM_SMCCC_KVM_FUNC_PTP 1 106 107 #define ARM_SMCCC_KVM_FUNC_FEATURES_2 127 107 108 #define ARM_SMCCC_KVM_NUM_FUNCS 128 108 109 ··· 114 113 ARM_SMCCC_KVM_FUNC_FEATURES) 115 114 116 115 #define SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED 1 116 + 117 + /* 118 + * ptp_kvm is a feature used for time sync between vm and host. 119 + * ptp_kvm module in guest kernel will get service from host using 120 + * this hypercall ID. 121 + */ 122 + #define ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID \ 123 + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ 124 + ARM_SMCCC_SMC_32, \ 125 + ARM_SMCCC_OWNER_VENDOR_HYP, \ 126 + ARM_SMCCC_KVM_FUNC_PTP) 127 + 128 + /* ptp_kvm counter type ID */ 129 + #define KVM_PTP_VIRT_COUNTER 0 130 + #define KVM_PTP_PHYS_COUNTER 1 117 131 118 132 /* Paravirtualised time calls (defined by ARM DEN0057A) */ 119 133 #define ARM_SMCCC_HV_PV_TIME_FEATURES \
+1
include/uapi/linux/kvm.h
··· 1078 1078 #define KVM_CAP_DIRTY_LOG_RING 192 1079 1079 #define KVM_CAP_X86_BUS_LOCK_EXIT 193 1080 1080 #define KVM_CAP_PPC_DAWR1 194 1081 + #define KVM_CAP_PTP_KVM 195 1081 1082 1082 1083 #ifdef KVM_CAP_IRQ_ROUTING 1083 1084