mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-10 23:20:05 +00:00
Merge branch 'kvm-arm64/ptp' into kvmarm-master/next
Signed-off-by: Marc Zyngier <maz@kernel.org>
This commit is contained in:
commit
d8f37d291c
@ -6737,3 +6737,13 @@ vcpu_info is set.
|
||||
The KVM_XEN_HVM_CONFIG_RUNSTATE flag indicates that the runstate-related
|
||||
features KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR/_CURRENT/_DATA/_ADJUST are
|
||||
supported by the KVM_XEN_VCPU_SET_ATTR/KVM_XEN_VCPU_GET_ATTR ioctls.
|
||||
|
||||
8.31 KVM_CAP_PTP_KVM
|
||||
--------------------
|
||||
|
||||
:Architectures: arm64
|
||||
|
||||
This capability indicates that the KVM virtual PTP service is
|
||||
supported in the host. A VMM can check whether the service is
|
||||
available to the guest on migration.
|
||||
|
||||
|
@ -10,3 +10,4 @@ ARM
|
||||
hyp-abi
|
||||
psci
|
||||
pvtime
|
||||
ptp_kvm
|
||||
|
25
Documentation/virt/kvm/arm/ptp_kvm.rst
Normal file
25
Documentation/virt/kvm/arm/ptp_kvm.rst
Normal file
@ -0,0 +1,25 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
PTP_KVM support for arm/arm64
|
||||
=============================
|
||||
|
||||
PTP_KVM is used for high precision time sync between host and guests.
|
||||
It relies on transferring the wall clock and counter value from the
|
||||
host to the guest using a KVM-specific hypercall.
|
||||
|
||||
* ARM_SMCCC_HYP_KVM_PTP_FUNC_ID: 0x86000001
|
||||
|
||||
This hypercall uses the SMC32/HVC32 calling convention:
|
||||
|
||||
ARM_SMCCC_HYP_KVM_PTP_FUNC_ID
|
||||
============== ======== =====================================
|
||||
Function ID: (uint32) 0x86000001
|
||||
Arguments: (uint32) KVM_PTP_VIRT_COUNTER(0)
|
||||
KVM_PTP_PHYS_COUNTER(1)
|
||||
Return Values: (int32) NOT_SUPPORTED(-1) on error, or
|
||||
(uint32) Upper 32 bits of wall clock time (r0)
|
||||
(uint32) Lower 32 bits of wall clock time (r1)
|
||||
(uint32) Upper 32 bits of counter (r2)
|
||||
(uint32) Lower 32 bits of counter (r3)
|
||||
Endianness: No Restrictions.
|
||||
============== ======== =====================================
|
@ -4,4 +4,7 @@
|
||||
|
||||
#include <asm/xen/hypervisor.h>
|
||||
|
||||
void kvm_init_hyp_services(void);
|
||||
bool kvm_arm_hyp_service_available(u32 func_id);
|
||||
|
||||
#endif
|
||||
|
@ -4,4 +4,7 @@
|
||||
|
||||
#include <asm/xen/hypervisor.h>
|
||||
|
||||
void kvm_init_hyp_services(void);
|
||||
bool kvm_arm_hyp_service_available(u32 func_id);
|
||||
|
||||
#endif
|
||||
|
@ -206,6 +206,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
case KVM_CAP_ARM_INJECT_EXT_DABT:
|
||||
case KVM_CAP_SET_GUEST_DEBUG:
|
||||
case KVM_CAP_VCPU_ATTRIBUTES:
|
||||
case KVM_CAP_PTP_KVM:
|
||||
r = 1;
|
||||
break;
|
||||
case KVM_CAP_ARM_SET_DEVICE_ADDR:
|
||||
|
@ -9,16 +9,65 @@
|
||||
#include <kvm/arm_hypercalls.h>
|
||||
#include <kvm/arm_psci.h>
|
||||
|
||||
static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
|
||||
{
|
||||
struct system_time_snapshot systime_snapshot;
|
||||
u64 cycles = ~0UL;
|
||||
u32 feature;
|
||||
|
||||
/*
|
||||
* system time and counter value must captured at the same
|
||||
* time to keep consistency and precision.
|
||||
*/
|
||||
ktime_get_snapshot(&systime_snapshot);
|
||||
|
||||
/*
|
||||
* This is only valid if the current clocksource is the
|
||||
* architected counter, as this is the only one the guest
|
||||
* can see.
|
||||
*/
|
||||
if (systime_snapshot.cs_id != CSID_ARM_ARCH_COUNTER)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The guest selects one of the two reference counters
|
||||
* (virtual or physical) with the first argument of the SMCCC
|
||||
* call. In case the identifier is not supported, error out.
|
||||
*/
|
||||
feature = smccc_get_arg1(vcpu);
|
||||
switch (feature) {
|
||||
case KVM_PTP_VIRT_COUNTER:
|
||||
cycles = systime_snapshot.cycles - vcpu_read_sys_reg(vcpu, CNTVOFF_EL2);
|
||||
break;
|
||||
case KVM_PTP_PHYS_COUNTER:
|
||||
cycles = systime_snapshot.cycles;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This relies on the top bit of val[0] never being set for
|
||||
* valid values of system time, because that is *really* far
|
||||
* in the future (about 292 years from 1970, and at that stage
|
||||
* nobody will give a damn about it).
|
||||
*/
|
||||
val[0] = upper_32_bits(systime_snapshot.real);
|
||||
val[1] = lower_32_bits(systime_snapshot.real);
|
||||
val[2] = upper_32_bits(cycles);
|
||||
val[3] = lower_32_bits(cycles);
|
||||
}
|
||||
|
||||
int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u32 func_id = smccc_get_function(vcpu);
|
||||
long val = SMCCC_RET_NOT_SUPPORTED;
|
||||
u64 val[4] = {SMCCC_RET_NOT_SUPPORTED};
|
||||
u32 feature;
|
||||
gpa_t gpa;
|
||||
|
||||
switch (func_id) {
|
||||
case ARM_SMCCC_VERSION_FUNC_ID:
|
||||
val = ARM_SMCCC_VERSION_1_1;
|
||||
val[0] = ARM_SMCCC_VERSION_1_1;
|
||||
break;
|
||||
case ARM_SMCCC_ARCH_FEATURES_FUNC_ID:
|
||||
feature = smccc_get_arg1(vcpu);
|
||||
@ -28,10 +77,10 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
|
||||
case SPECTRE_VULNERABLE:
|
||||
break;
|
||||
case SPECTRE_MITIGATED:
|
||||
val = SMCCC_RET_SUCCESS;
|
||||
val[0] = SMCCC_RET_SUCCESS;
|
||||
break;
|
||||
case SPECTRE_UNAFFECTED:
|
||||
val = SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED;
|
||||
val[0] = SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -54,22 +103,35 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
|
||||
break;
|
||||
fallthrough;
|
||||
case SPECTRE_UNAFFECTED:
|
||||
val = SMCCC_RET_NOT_REQUIRED;
|
||||
val[0] = SMCCC_RET_NOT_REQUIRED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ARM_SMCCC_HV_PV_TIME_FEATURES:
|
||||
val = SMCCC_RET_SUCCESS;
|
||||
val[0] = SMCCC_RET_SUCCESS;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ARM_SMCCC_HV_PV_TIME_FEATURES:
|
||||
val = kvm_hypercall_pv_features(vcpu);
|
||||
val[0] = kvm_hypercall_pv_features(vcpu);
|
||||
break;
|
||||
case ARM_SMCCC_HV_PV_TIME_ST:
|
||||
gpa = kvm_init_stolen_time(vcpu);
|
||||
if (gpa != GPA_INVALID)
|
||||
val = gpa;
|
||||
val[0] = gpa;
|
||||
break;
|
||||
case ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID:
|
||||
val[0] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0;
|
||||
val[1] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1;
|
||||
val[2] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2;
|
||||
val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
|
||||
break;
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
|
||||
val[0] = BIT(ARM_SMCCC_KVM_FUNC_FEATURES);
|
||||
val[0] |= BIT(ARM_SMCCC_KVM_FUNC_PTP);
|
||||
break;
|
||||
case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
|
||||
kvm_ptp_get_time(vcpu, val);
|
||||
break;
|
||||
case ARM_SMCCC_TRNG_VERSION:
|
||||
case ARM_SMCCC_TRNG_FEATURES:
|
||||
@ -81,6 +143,6 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
|
||||
return kvm_psci_call(vcpu);
|
||||
}
|
||||
|
||||
smccc_set_retval(vcpu, val, 0, 0, 0);
|
||||
smccc_set_retval(vcpu, val[0], val[1], val[2], val[3]);
|
||||
return 1;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/clocksource_ids.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
@ -24,6 +25,8 @@
|
||||
#include <linux/sched/clock.h>
|
||||
#include <linux/sched_clock.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/ptp_kvm.h>
|
||||
|
||||
#include <asm/arch_timer.h>
|
||||
#include <asm/virt.h>
|
||||
@ -191,6 +194,7 @@ static u64 arch_counter_read_cc(const struct cyclecounter *cc)
|
||||
|
||||
static struct clocksource clocksource_counter = {
|
||||
.name = "arch_sys_counter",
|
||||
.id = CSID_ARM_ARCH_COUNTER,
|
||||
.rating = 400,
|
||||
.read = arch_counter_read,
|
||||
.mask = CLOCKSOURCE_MASK(56),
|
||||
@ -1657,3 +1661,35 @@ static int __init arch_timer_acpi_init(struct acpi_table_header *table)
|
||||
}
|
||||
TIMER_ACPI_DECLARE(arch_timer, ACPI_SIG_GTDT, arch_timer_acpi_init);
|
||||
#endif
|
||||
|
||||
int kvm_arch_ptp_get_crosststamp(u64 *cycle, struct timespec64 *ts,
|
||||
struct clocksource **cs)
|
||||
{
|
||||
struct arm_smccc_res hvc_res;
|
||||
u32 ptp_counter;
|
||||
ktime_t ktime;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_HAVE_ARM_SMCCC_DISCOVERY))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI)
|
||||
ptp_counter = KVM_PTP_VIRT_COUNTER;
|
||||
else
|
||||
ptp_counter = KVM_PTP_PHYS_COUNTER;
|
||||
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
|
||||
ptp_counter, &hvc_res);
|
||||
|
||||
if ((int)(hvc_res.a0) < 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ktime = (u64)hvc_res.a0 << 32 | hvc_res.a1;
|
||||
*ts = ktime_to_timespec64(ktime);
|
||||
if (cycle)
|
||||
*cycle = (u64)hvc_res.a2 << 32 | hvc_res.a3;
|
||||
if (cs)
|
||||
*cs = &clocksource_counter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_arch_ptp_get_crosststamp);
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/hypervisor.h>
|
||||
#include <asm/system_misc.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/suspend.h>
|
||||
@ -498,6 +499,7 @@ static int __init psci_probe(void)
|
||||
psci_init_cpu_suspend();
|
||||
psci_init_system_suspend();
|
||||
psci_init_system_reset2();
|
||||
kvm_init_hyp_services();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1,4 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smccc.o
|
||||
obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smccc.o kvm_guest.o
|
||||
obj-$(CONFIG_ARM_SMCCC_SOC_ID) += soc_id.o
|
||||
|
50
drivers/firmware/smccc/kvm_guest.c
Normal file
50
drivers/firmware/smccc/kvm_guest.c
Normal file
@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#define pr_fmt(fmt) "smccc: KVM: " fmt
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <asm/hypervisor.h>
|
||||
|
||||
static DECLARE_BITMAP(__kvm_arm_hyp_services, ARM_SMCCC_KVM_NUM_FUNCS) __ro_after_init = { };
|
||||
|
||||
void __init kvm_init_hyp_services(void)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
u32 val[4];
|
||||
|
||||
if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_HVC)
|
||||
return;
|
||||
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, &res);
|
||||
if (res.a0 != ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0 ||
|
||||
res.a1 != ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1 ||
|
||||
res.a2 != ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2 ||
|
||||
res.a3 != ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3)
|
||||
return;
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID, &res);
|
||||
|
||||
val[0] = lower_32_bits(res.a0);
|
||||
val[1] = lower_32_bits(res.a1);
|
||||
val[2] = lower_32_bits(res.a2);
|
||||
val[3] = lower_32_bits(res.a3);
|
||||
|
||||
bitmap_from_arr32(__kvm_arm_hyp_services, val, ARM_SMCCC_KVM_NUM_FUNCS);
|
||||
|
||||
pr_info("hypervisor services detected (0x%08lx 0x%08lx 0x%08lx 0x%08lx)\n",
|
||||
res.a3, res.a2, res.a1, res.a0);
|
||||
}
|
||||
|
||||
bool kvm_arm_hyp_service_available(u32 func_id)
|
||||
{
|
||||
if (func_id >= ARM_SMCCC_KVM_NUM_FUNCS)
|
||||
return false;
|
||||
|
||||
return test_bit(func_id, __kvm_arm_hyp_services);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kvm_arm_hyp_service_available);
|
@ -8,6 +8,7 @@
|
||||
#include <linux/cache.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/archrandom.h>
|
||||
|
||||
static u32 smccc_version = ARM_SMCCC_VERSION_1_0;
|
||||
|
@ -108,7 +108,7 @@ config PTP_1588_CLOCK_PCH
|
||||
config PTP_1588_CLOCK_KVM
|
||||
tristate "KVM virtual PTP clock"
|
||||
depends on PTP_1588_CLOCK
|
||||
depends on KVM_GUEST && X86
|
||||
depends on (KVM_GUEST && X86) || (HAVE_ARM_SMCCC_DISCOVERY && ARM_ARCH_TIMER)
|
||||
default y
|
||||
help
|
||||
This driver adds support for using kvm infrastructure as a PTP
|
||||
|
@ -4,6 +4,8 @@
|
||||
#
|
||||
|
||||
ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o
|
||||
ptp_kvm-$(CONFIG_X86) := ptp_kvm_x86.o ptp_kvm_common.o
|
||||
ptp_kvm-$(CONFIG_HAVE_ARM_SMCCC) := ptp_kvm_arm.o ptp_kvm_common.o
|
||||
obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o
|
||||
obj-$(CONFIG_PTP_1588_CLOCK_DTE) += ptp_dte.o
|
||||
obj-$(CONFIG_PTP_1588_CLOCK_INES) += ptp_ines.o
|
||||
|
28
drivers/ptp/ptp_kvm_arm.c
Normal file
28
drivers/ptp/ptp_kvm_arm.c
Normal file
@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Virtual PTP 1588 clock for use with KVM guests
|
||||
* Copyright (C) 2019 ARM Ltd.
|
||||
* All Rights Reserved
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/ptp_kvm.h>
|
||||
|
||||
#include <asm/arch_timer.h>
|
||||
#include <asm/hypervisor.h>
|
||||
|
||||
int kvm_arch_ptp_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = kvm_arm_hyp_service_available(ARM_SMCCC_KVM_FUNC_PTP);
|
||||
if (ret <= 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_ptp_get_clock(struct timespec64 *ts)
|
||||
{
|
||||
return kvm_arch_ptp_get_crosststamp(NULL, ts, NULL);
|
||||
}
|
@ -8,11 +8,11 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ptp_kvm.h>
|
||||
#include <uapi/linux/kvm_para.h>
|
||||
#include <asm/kvm_para.h>
|
||||
#include <asm/pvclock.h>
|
||||
#include <asm/kvmclock.h>
|
||||
#include <uapi/asm/kvm_para.h>
|
||||
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
@ -24,56 +24,29 @@ struct kvm_ptp_clock {
|
||||
|
||||
static DEFINE_SPINLOCK(kvm_ptp_lock);
|
||||
|
||||
static struct pvclock_vsyscall_time_info *hv_clock;
|
||||
|
||||
static struct kvm_clock_pairing clock_pair;
|
||||
static phys_addr_t clock_pair_gpa;
|
||||
|
||||
static int ptp_kvm_get_time_fn(ktime_t *device_time,
|
||||
struct system_counterval_t *system_counter,
|
||||
void *ctx)
|
||||
{
|
||||
unsigned long ret;
|
||||
long ret;
|
||||
u64 cycle;
|
||||
struct timespec64 tspec;
|
||||
unsigned version;
|
||||
int cpu;
|
||||
struct pvclock_vcpu_time_info *src;
|
||||
struct clocksource *cs;
|
||||
|
||||
spin_lock(&kvm_ptp_lock);
|
||||
|
||||
preempt_disable_notrace();
|
||||
cpu = smp_processor_id();
|
||||
src = &hv_clock[cpu].pvti;
|
||||
|
||||
do {
|
||||
/*
|
||||
* We are using a TSC value read in the hosts
|
||||
* kvm_hc_clock_pairing handling.
|
||||
* So any changes to tsc_to_system_mul
|
||||
* and tsc_shift or any other pvclock
|
||||
* data invalidate that measurement.
|
||||
*/
|
||||
version = pvclock_read_begin(src);
|
||||
|
||||
ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING,
|
||||
clock_pair_gpa,
|
||||
KVM_CLOCK_PAIRING_WALLCLOCK);
|
||||
if (ret != 0) {
|
||||
pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret);
|
||||
spin_unlock(&kvm_ptp_lock);
|
||||
preempt_enable_notrace();
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
tspec.tv_sec = clock_pair.sec;
|
||||
tspec.tv_nsec = clock_pair.nsec;
|
||||
ret = __pvclock_read_cycles(src, clock_pair.tsc);
|
||||
} while (pvclock_read_retry(src, version));
|
||||
ret = kvm_arch_ptp_get_crosststamp(&cycle, &tspec, &cs);
|
||||
if (ret) {
|
||||
spin_unlock(&kvm_ptp_lock);
|
||||
preempt_enable_notrace();
|
||||
return ret;
|
||||
}
|
||||
|
||||
preempt_enable_notrace();
|
||||
|
||||
system_counter->cycles = ret;
|
||||
system_counter->cs = &kvm_clock;
|
||||
system_counter->cycles = cycle;
|
||||
system_counter->cs = cs;
|
||||
|
||||
*device_time = timespec64_to_ktime(tspec);
|
||||
|
||||
@ -111,22 +84,17 @@ static int ptp_kvm_settime(struct ptp_clock_info *ptp,
|
||||
|
||||
static int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
|
||||
{
|
||||
unsigned long ret;
|
||||
long ret;
|
||||
struct timespec64 tspec;
|
||||
|
||||
spin_lock(&kvm_ptp_lock);
|
||||
|
||||
ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING,
|
||||
clock_pair_gpa,
|
||||
KVM_CLOCK_PAIRING_WALLCLOCK);
|
||||
if (ret != 0) {
|
||||
pr_err_ratelimited("clock offset hypercall ret %lu\n", ret);
|
||||
ret = kvm_arch_ptp_get_clock(&tspec);
|
||||
if (ret) {
|
||||
spin_unlock(&kvm_ptp_lock);
|
||||
return -EOPNOTSUPP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
tspec.tv_sec = clock_pair.sec;
|
||||
tspec.tv_nsec = clock_pair.nsec;
|
||||
spin_unlock(&kvm_ptp_lock);
|
||||
|
||||
memcpy(ts, &tspec, sizeof(struct timespec64));
|
||||
@ -168,19 +136,11 @@ static int __init ptp_kvm_init(void)
|
||||
{
|
||||
long ret;
|
||||
|
||||
if (!kvm_para_available())
|
||||
return -ENODEV;
|
||||
|
||||
clock_pair_gpa = slow_virt_to_phys(&clock_pair);
|
||||
hv_clock = pvclock_get_pvti_cpu0_va();
|
||||
|
||||
if (!hv_clock)
|
||||
return -ENODEV;
|
||||
|
||||
ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa,
|
||||
KVM_CLOCK_PAIRING_WALLCLOCK);
|
||||
if (ret == -KVM_ENOSYS || ret == -KVM_EOPNOTSUPP)
|
||||
return -ENODEV;
|
||||
ret = kvm_arch_ptp_init();
|
||||
if (ret) {
|
||||
pr_err("fail to initialize ptp_kvm");
|
||||
return ret;
|
||||
}
|
||||
|
||||
kvm_ptp_clock.caps = ptp_kvm_caps;
|
||||
|
97
drivers/ptp/ptp_kvm_x86.c
Normal file
97
drivers/ptp/ptp_kvm_x86.c
Normal file
@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Virtual PTP 1588 clock for use with KVM guests
|
||||
*
|
||||
* Copyright (C) 2017 Red Hat Inc.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/pvclock.h>
|
||||
#include <asm/kvmclock.h>
|
||||
#include <linux/module.h>
|
||||
#include <uapi/asm/kvm_para.h>
|
||||
#include <uapi/linux/kvm_para.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
#include <linux/ptp_kvm.h>
|
||||
|
||||
struct pvclock_vsyscall_time_info *hv_clock;
|
||||
|
||||
static phys_addr_t clock_pair_gpa;
|
||||
static struct kvm_clock_pairing clock_pair;
|
||||
|
||||
int kvm_arch_ptp_init(void)
|
||||
{
|
||||
long ret;
|
||||
|
||||
if (!kvm_para_available())
|
||||
return -ENODEV;
|
||||
|
||||
clock_pair_gpa = slow_virt_to_phys(&clock_pair);
|
||||
hv_clock = pvclock_get_pvti_cpu0_va();
|
||||
if (!hv_clock)
|
||||
return -ENODEV;
|
||||
|
||||
ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING, clock_pair_gpa,
|
||||
KVM_CLOCK_PAIRING_WALLCLOCK);
|
||||
if (ret == -KVM_ENOSYS || ret == -KVM_EOPNOTSUPP)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_ptp_get_clock(struct timespec64 *ts)
|
||||
{
|
||||
long ret;
|
||||
|
||||
ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING,
|
||||
clock_pair_gpa,
|
||||
KVM_CLOCK_PAIRING_WALLCLOCK);
|
||||
if (ret != 0) {
|
||||
pr_err_ratelimited("clock offset hypercall ret %lu\n", ret);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
ts->tv_sec = clock_pair.sec;
|
||||
ts->tv_nsec = clock_pair.nsec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_arch_ptp_get_crosststamp(u64 *cycle, struct timespec64 *tspec,
|
||||
struct clocksource **cs)
|
||||
{
|
||||
struct pvclock_vcpu_time_info *src;
|
||||
unsigned int version;
|
||||
long ret;
|
||||
int cpu;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
src = &hv_clock[cpu].pvti;
|
||||
|
||||
do {
|
||||
/*
|
||||
* We are using a TSC value read in the hosts
|
||||
* kvm_hc_clock_pairing handling.
|
||||
* So any changes to tsc_to_system_mul
|
||||
* and tsc_shift or any other pvclock
|
||||
* data invalidate that measurement.
|
||||
*/
|
||||
version = pvclock_read_begin(src);
|
||||
|
||||
ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING,
|
||||
clock_pair_gpa,
|
||||
KVM_CLOCK_PAIRING_WALLCLOCK);
|
||||
if (ret != 0) {
|
||||
pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
tspec->tv_sec = clock_pair.sec;
|
||||
tspec->tv_nsec = clock_pair.nsec;
|
||||
*cycle = __pvclock_read_cycles(src, clock_pair.tsc);
|
||||
} while (pvclock_read_retry(src, version));
|
||||
|
||||
*cs = &kvm_clock;
|
||||
|
||||
return 0;
|
||||
}
|
@ -55,6 +55,8 @@
|
||||
#define ARM_SMCCC_OWNER_TRUSTED_OS 50
|
||||
#define ARM_SMCCC_OWNER_TRUSTED_OS_END 63
|
||||
|
||||
#define ARM_SMCCC_FUNC_QUERY_CALL_UID 0xff01
|
||||
|
||||
#define ARM_SMCCC_QUIRK_NONE 0
|
||||
#define ARM_SMCCC_QUIRK_QCOM_A6 1 /* Save/restore register a6 */
|
||||
|
||||
@ -87,8 +89,47 @@
|
||||
ARM_SMCCC_SMC_32, \
|
||||
0, 0x7fff)
|
||||
|
||||
#define ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_32, \
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, \
|
||||
ARM_SMCCC_FUNC_QUERY_CALL_UID)
|
||||
|
||||
/* KVM UID value: 28b46fb6-2ec5-11e9-a9ca-4b564d003a74 */
|
||||
#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0 0xb66fb428U
|
||||
#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1 0xe911c52eU
|
||||
#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2 0x564bcaa9U
|
||||
#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3 0x743a004dU
|
||||
|
||||
/* KVM "vendor specific" services */
|
||||
#define ARM_SMCCC_KVM_FUNC_FEATURES 0
|
||||
#define ARM_SMCCC_KVM_FUNC_PTP 1
|
||||
#define ARM_SMCCC_KVM_FUNC_FEATURES_2 127
|
||||
#define ARM_SMCCC_KVM_NUM_FUNCS 128
|
||||
|
||||
#define ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_32, \
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, \
|
||||
ARM_SMCCC_KVM_FUNC_FEATURES)
|
||||
|
||||
#define SMCCC_ARCH_WORKAROUND_RET_UNAFFECTED 1
|
||||
|
||||
/*
|
||||
* ptp_kvm is a feature used for time sync between vm and host.
|
||||
* ptp_kvm module in guest kernel will get service from host using
|
||||
* this hypercall ID.
|
||||
*/
|
||||
#define ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_32, \
|
||||
ARM_SMCCC_OWNER_VENDOR_HYP, \
|
||||
ARM_SMCCC_KVM_FUNC_PTP)
|
||||
|
||||
/* ptp_kvm counter type ID */
|
||||
#define KVM_PTP_VIRT_COUNTER 0
|
||||
#define KVM_PTP_PHYS_COUNTER 1
|
||||
|
||||
/* Paravirtualised time calls (defined by ARM DEN0057A) */
|
||||
#define ARM_SMCCC_HV_PV_TIME_FEATURES \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/timer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clocksource_ids.h>
|
||||
#include <asm/div64.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
@ -62,6 +63,10 @@ struct module;
|
||||
* 400-499: Perfect
|
||||
* The ideal clocksource. A must-use where
|
||||
* available.
|
||||
* @id: Defaults to CSID_GENERIC. The id value is captured
|
||||
* in certain snapshot functions to allow callers to
|
||||
* validate the clocksource from which the snapshot was
|
||||
* taken.
|
||||
* @flags: Flags describing special properties
|
||||
* @enable: Optional function to enable the clocksource
|
||||
* @disable: Optional function to disable the clocksource
|
||||
@ -100,6 +105,7 @@ struct clocksource {
|
||||
const char *name;
|
||||
struct list_head list;
|
||||
int rating;
|
||||
enum clocksource_ids id;
|
||||
enum vdso_clock_mode vdso_clock_mode;
|
||||
unsigned long flags;
|
||||
|
||||
|
12
include/linux/clocksource_ids.h
Normal file
12
include/linux/clocksource_ids.h
Normal file
@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_CLOCKSOURCE_IDS_H
|
||||
#define _LINUX_CLOCKSOURCE_IDS_H
|
||||
|
||||
/* Enum to give clocksources a unique identifier */
|
||||
enum clocksource_ids {
|
||||
CSID_GENERIC = 0,
|
||||
CSID_ARM_ARCH_COUNTER,
|
||||
CSID_MAX,
|
||||
};
|
||||
|
||||
#endif
|
19
include/linux/ptp_kvm.h
Normal file
19
include/linux/ptp_kvm.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Virtual PTP 1588 clock for use with KVM guests
|
||||
*
|
||||
* Copyright (C) 2017 Red Hat Inc.
|
||||
*/
|
||||
|
||||
#ifndef _PTP_KVM_H_
|
||||
#define _PTP_KVM_H_
|
||||
|
||||
struct timespec64;
|
||||
struct clocksource;
|
||||
|
||||
int kvm_arch_ptp_init(void);
|
||||
int kvm_arch_ptp_get_clock(struct timespec64 *ts);
|
||||
int kvm_arch_ptp_get_crosststamp(u64 *cycle,
|
||||
struct timespec64 *tspec, struct clocksource **cs);
|
||||
|
||||
#endif /* _PTP_KVM_H_ */
|
@ -3,6 +3,7 @@
|
||||
#define _LINUX_TIMEKEEPING_H
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/clocksource_ids.h>
|
||||
|
||||
/* Included from linux/ktime.h */
|
||||
|
||||
@ -243,11 +244,12 @@ struct ktime_timestamps {
|
||||
* @cs_was_changed_seq: The sequence number of clocksource change events
|
||||
*/
|
||||
struct system_time_snapshot {
|
||||
u64 cycles;
|
||||
ktime_t real;
|
||||
ktime_t raw;
|
||||
unsigned int clock_was_set_seq;
|
||||
u8 cs_was_changed_seq;
|
||||
u64 cycles;
|
||||
ktime_t real;
|
||||
ktime_t raw;
|
||||
enum clocksource_ids cs_id;
|
||||
unsigned int clock_was_set_seq;
|
||||
u8 cs_was_changed_seq;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1078,6 +1078,7 @@ struct kvm_ppc_resize_hpt {
|
||||
#define KVM_CAP_DIRTY_LOG_RING 192
|
||||
#define KVM_CAP_X86_BUS_LOCK_EXIT 193
|
||||
#define KVM_CAP_PPC_DAWR1 194
|
||||
#define KVM_CAP_PTP_KVM 195
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
|
@ -920,6 +920,8 @@ int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)
|
||||
|
||||
clocksource_arch_init(cs);
|
||||
|
||||
if (WARN_ON_ONCE((unsigned int)cs->id >= CSID_MAX))
|
||||
cs->id = CSID_GENERIC;
|
||||
if (cs->vdso_clock_mode < 0 ||
|
||||
cs->vdso_clock_mode >= VDSO_CLOCKMODE_MAX) {
|
||||
pr_warn("clocksource %s registered with invalid VDSO mode %d. Disabling VDSO support.\n",
|
||||
|
@ -1048,6 +1048,7 @@ void ktime_get_snapshot(struct system_time_snapshot *systime_snapshot)
|
||||
do {
|
||||
seq = read_seqcount_begin(&tk_core.seq);
|
||||
now = tk_clock_read(&tk->tkr_mono);
|
||||
systime_snapshot->cs_id = tk->tkr_mono.clock->id;
|
||||
systime_snapshot->cs_was_changed_seq = tk->cs_was_changed_seq;
|
||||
systime_snapshot->clock_was_set_seq = tk->clock_was_set_seq;
|
||||
base_real = ktime_add(tk->tkr_mono.base,
|
||||
|
Loading…
x
Reference in New Issue
Block a user