mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-04-27 11:31:05 +00:00
kernel: PTP_KVM support for arm/arm64 in Kata
This work patched the 4.19, 5.4 and 5.10 kernels, and now ptp_kvm can work correctly when the host and guest use different kernel versions.. Fixes: #2123 Signed-off-by: Damon Kwok <damon-kwok@outlook.com>
This commit is contained in:
parent
594ff3a5bd
commit
4fe23b190f
@ -1,457 +0,0 @@
|
||||
From bee1ae5587a7427dbb9e9e313f6d0a43a9e0ec2e Mon Sep 17 00:00:00 2001
|
||||
From: Jianyong Wu <jianyong.wu@arm.com>
|
||||
Date: Mon, 30 Sep 2019 09:26:22 +0800
|
||||
Subject: [PATCH] 4.19: enable ptp_kvm for arm64 in kata
|
||||
|
||||
---
|
||||
drivers/clocksource/arm_arch_timer.c | 25 ++++++
|
||||
drivers/ptp/Kconfig | 2 +-
|
||||
drivers/ptp/Makefile | 1 +
|
||||
drivers/ptp/ptp_kvm_arm64.c | 59 ++++++++++++++
|
||||
drivers/ptp/{ptp_kvm.c => ptp_kvm_common.c} | 89 +++++----------------
|
||||
drivers/ptp/ptp_kvm_x86.c | 87 ++++++++++++++++++++
|
||||
include/asm-generic/ptp_kvm.h | 12 +++
|
||||
include/linux/arm-smccc.h | 5 ++
|
||||
virt/kvm/arm/psci.c | 12 +++
|
||||
9 files changed, 221 insertions(+), 71 deletions(-)
|
||||
create mode 100644 drivers/ptp/ptp_kvm_arm64.c
|
||||
rename drivers/ptp/{ptp_kvm.c => ptp_kvm_common.c} (56%)
|
||||
create mode 100644 drivers/ptp/ptp_kvm_x86.c
|
||||
create mode 100644 include/asm-generic/ptp_kvm.h
|
||||
|
||||
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
|
||||
index d8c7f5750cdb..84ba8f9e57be 100644
|
||||
--- a/drivers/clocksource/arm_arch_timer.c
|
||||
+++ b/drivers/clocksource/arm_arch_timer.c
|
||||
@@ -1571,3 +1571,28 @@ 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
|
||||
+
|
||||
+#if IS_ENABLED(CONFIG_PTP_1588_CLOCK_KVM)
|
||||
+#include <linux/arm-smccc.h>
|
||||
+int kvm_arch_ptp_get_clock_fn(long *cycle, struct timespec64 *ts,
|
||||
+ struct clocksource **cs)
|
||||
+{
|
||||
+ struct arm_smccc_res hvc_res;
|
||||
+ ktime_t ktime_overall;
|
||||
+ struct arm_smccc_quirk hvc_quirk;
|
||||
+
|
||||
+ __arm_smccc_hvc(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, 0, 0, 0, 0, 0, 0, 0, &hvc_res, &hvc_quirk);
|
||||
+
|
||||
+ if ((long)(hvc_res.a0) < 0)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ ts->tv_sec = hvc_res.a0;
|
||||
+ ts->tv_nsec = hvc_res.a1;
|
||||
+ *cycle = hvc_res.a2 << 32 | hvc_res.a3;
|
||||
+ *cs = &clocksource_counter;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(kvm_arch_ptp_get_clock_fn);
|
||||
+#endif
|
||||
+
|
||||
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
|
||||
index d137c480db46..318b3f5df1ea 100644
|
||||
--- a/drivers/ptp/Kconfig
|
||||
+++ b/drivers/ptp/Kconfig
|
||||
@@ -109,7 +109,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 || ARM64
|
||||
default y
|
||||
help
|
||||
This driver adds support for using kvm infrastructure as a PTP
|
||||
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
|
||||
index 19efa9cfa950..1bf4940a88a6 100644
|
||||
--- a/drivers/ptp/Makefile
|
||||
+++ b/drivers/ptp/Makefile
|
||||
@@ -4,6 +4,7 @@
|
||||
#
|
||||
|
||||
ptp-y := ptp_clock.o ptp_chardev.o ptp_sysfs.o
|
||||
+ptp_kvm-y := ptp_kvm_common.o ptp_kvm_$(ARCH).o
|
||||
obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o
|
||||
obj-$(CONFIG_PTP_1588_CLOCK_DTE) += ptp_dte.o
|
||||
obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o
|
||||
diff --git a/drivers/ptp/ptp_kvm_arm64.c b/drivers/ptp/ptp_kvm_arm64.c
|
||||
new file mode 100644
|
||||
index 000000000000..fcd83324c7e1
|
||||
--- /dev/null
|
||||
+++ b/drivers/ptp/ptp_kvm_arm64.c
|
||||
@@ -0,0 +1,59 @@
|
||||
+// 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/kernel.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <asm/hypervisor.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/psci.h>
|
||||
+#include <linux/arm-smccc.h>
|
||||
+#include <linux/timecounter.h>
|
||||
+#include <linux/sched/clock.h>
|
||||
+#include <asm/arch_timer.h>
|
||||
+
|
||||
+
|
||||
+void arm_smccc_1_1_invoke(u32 id, struct arm_smccc_res *res)
|
||||
+{
|
||||
+ struct arm_smccc_quirk hvc_quirk;
|
||||
+
|
||||
+ __arm_smccc_hvc(id, 0, 0, 0, 0, 0, 0, 0, res, &hvc_quirk);
|
||||
+}
|
||||
+
|
||||
+int kvm_arch_ptp_init(void)
|
||||
+{
|
||||
+ struct arm_smccc_res hvc_res;
|
||||
+
|
||||
+ arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
|
||||
+ &hvc_res);
|
||||
+ if ((long)(hvc_res.a0) < 0)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int kvm_arch_ptp_get_clock_generic(struct timespec64 *ts,
|
||||
+ struct arm_smccc_res *hvc_res)
|
||||
+{
|
||||
+ arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID,
|
||||
+ hvc_res);
|
||||
+ if ((long)(hvc_res->a0) < 0)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ ts->tv_sec = hvc_res->a0;
|
||||
+ ts->tv_nsec = hvc_res->a1;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int kvm_arch_ptp_get_clock(struct timespec64 *ts)
|
||||
+{
|
||||
+ struct arm_smccc_res hvc_res;
|
||||
+
|
||||
+ kvm_arch_ptp_get_clock_generic(ts, &hvc_res);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/drivers/ptp/ptp_kvm.c b/drivers/ptp/ptp_kvm_common.c
|
||||
similarity index 56%
|
||||
rename from drivers/ptp/ptp_kvm.c
|
||||
rename to drivers/ptp/ptp_kvm_common.c
|
||||
index c67dd11e08b1..c0b445fa6144 100644
|
||||
--- a/drivers/ptp/ptp_kvm.c
|
||||
+++ b/drivers/ptp/ptp_kvm_common.c
|
||||
@@ -1,29 +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.
|
||||
- *
|
||||
- * This program is free software; you can redistribute it and/or modify
|
||||
- * it under the terms of the GNU General Public License as published by
|
||||
- * the Free Software Foundation; either version 2 of the License, or
|
||||
- * (at your option) any later version.
|
||||
- *
|
||||
- * This program is distributed in the hope that it will be useful,
|
||||
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- * GNU General Public License for more details.
|
||||
- *
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
+#include <linux/slab.h>
|
||||
#include <linux/module.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 <asm-generic/ptp_kvm.h>
|
||||
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
|
||||
@@ -34,56 +24,29 @@ struct kvm_ptp_clock {
|
||||
|
||||
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;
|
||||
+ unsigned long ret, 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_clock_fn(&cycle, &tspec, &cs);
|
||||
+ if (ret != 0) {
|
||||
+ pr_err_ratelimited("clock pairing hypercall ret %lu\n", ret);
|
||||
+ spin_unlock(&kvm_ptp_lock);
|
||||
+ preempt_enable_notrace();
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
|
||||
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);
|
||||
|
||||
@@ -126,17 +89,13 @@ static int ptp_kvm_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
|
||||
|
||||
spin_lock(&kvm_ptp_lock);
|
||||
|
||||
- ret = kvm_hypercall2(KVM_HC_CLOCK_PAIRING,
|
||||
- clock_pair_gpa,
|
||||
- KVM_CLOCK_PAIRING_WALLCLOCK);
|
||||
+ ret = kvm_arch_ptp_get_clock(&tspec);
|
||||
if (ret != 0) {
|
||||
pr_err_ratelimited("clock offset hypercall ret %lu\n", ret);
|
||||
spin_unlock(&kvm_ptp_lock);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
- tspec.tv_sec = clock_pair.sec;
|
||||
- tspec.tv_nsec = clock_pair.nsec;
|
||||
spin_unlock(&kvm_ptp_lock);
|
||||
|
||||
memcpy(ts, &tspec, sizeof(struct timespec64));
|
||||
@@ -176,21 +135,11 @@ static void __exit ptp_kvm_exit(void)
|
||||
|
||||
static int __init ptp_kvm_init(void)
|
||||
{
|
||||
- long ret;
|
||||
-
|
||||
- if (!kvm_para_available())
|
||||
- return -ENODEV;
|
||||
+ int ret;
|
||||
|
||||
- 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)
|
||||
+ return -EOPNOTSUPP;
|
||||
|
||||
kvm_ptp_clock.caps = ptp_kvm_caps;
|
||||
|
||||
diff --git a/drivers/ptp/ptp_kvm_x86.c b/drivers/ptp/ptp_kvm_x86.c
|
||||
new file mode 100644
|
||||
index 000000000000..a52cf1c2990c
|
||||
--- /dev/null
|
||||
+++ b/drivers/ptp/ptp_kvm_x86.c
|
||||
@@ -0,0 +1,87 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * Virtual PTP 1588 clock for use with KVM guests
|
||||
+ *
|
||||
+ * Copyright (C) 2017 Red Hat Inc.
|
||||
+ */
|
||||
+
|
||||
+#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>
|
||||
+
|
||||
+phys_addr_t clock_pair_gpa;
|
||||
+struct kvm_clock_pairing clock_pair;
|
||||
+struct pvclock_vsyscall_time_info *hv_clock;
|
||||
+
|
||||
+int kvm_arch_ptp_init(void)
|
||||
+{
|
||||
+ int 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)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ ts->tv_sec = clock_pair.sec;
|
||||
+ ts->tv_nsec = clock_pair.nsec;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int kvm_arch_ptp_get_clock_fn(unsigned long *cycle, struct timespec64 *tspec,
|
||||
+ struct clocksource **cs)
|
||||
+{
|
||||
+ unsigned long ret;
|
||||
+ unsigned int version;
|
||||
+ int cpu;
|
||||
+ struct pvclock_vcpu_time_info *src;
|
||||
+
|
||||
+ 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);
|
||||
+ 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;
|
||||
+}
|
||||
diff --git a/include/asm-generic/ptp_kvm.h b/include/asm-generic/ptp_kvm.h
|
||||
new file mode 100644
|
||||
index 000000000000..883eea494a80
|
||||
--- /dev/null
|
||||
+++ b/include/asm-generic/ptp_kvm.h
|
||||
@@ -0,0 +1,12 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+/*
|
||||
+ * linux/drivers/clocksource/arm_arch_timer.c
|
||||
+ *
|
||||
+ * Copyright (C) 2019 ARM Ltd.
|
||||
+ * All Rights Reserved
|
||||
+ */
|
||||
+
|
||||
+int kvm_arch_ptp_init(void);
|
||||
+int kvm_arch_ptp_get_clock(struct timespec64 *ts);
|
||||
+int kvm_arch_ptp_get_clock_fn(unsigned long *cycle,
|
||||
+ struct timespec64 *tspec, void *cs);
|
||||
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
|
||||
index 18863d56273c..10e99c82d098 100644
|
||||
--- a/include/linux/arm-smccc.h
|
||||
+++ b/include/linux/arm-smccc.h
|
||||
@@ -75,6 +75,11 @@
|
||||
ARM_SMCCC_SMC_32, \
|
||||
0, 1)
|
||||
|
||||
+#define ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID \
|
||||
+ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
+ ARM_SMCCC_SMC_32, \
|
||||
+ 0, 2)
|
||||
+
|
||||
#define ARM_SMCCC_ARCH_WORKAROUND_1 \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
ARM_SMCCC_SMC_32, \
|
||||
diff --git a/virt/kvm/arm/psci.c b/virt/kvm/arm/psci.c
|
||||
index 9b73d3ad918a..9b9999bdeab7 100644
|
||||
--- a/virt/kvm/arm/psci.c
|
||||
+++ b/virt/kvm/arm/psci.c
|
||||
@@ -407,6 +407,9 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
|
||||
u32 func_id = smccc_get_function(vcpu);
|
||||
u32 val = SMCCC_RET_NOT_SUPPORTED;
|
||||
u32 feature;
|
||||
+ struct timespec64 ts;
|
||||
+ u64 cycles, cycle_high, cycle_low;
|
||||
+ struct system_time_snapshot systime_snapshot;
|
||||
|
||||
switch (func_id) {
|
||||
case ARM_SMCCC_VERSION_FUNC_ID:
|
||||
@@ -435,6 +438,15 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
+ case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
|
||||
+ ktime_get_real_ts64(&ts);
|
||||
+ ktime_get_snapshot(&systime_snapshot);
|
||||
+ cycles = systime_snapshot.cycles - vcpu_vtimer(vcpu)->cntvoff;
|
||||
+ cycle_high = cycles >> 32;
|
||||
+ cycle_low = cycles << 32 >> 32;
|
||||
+
|
||||
+ smccc_set_retval(vcpu, ts.tv_sec, ts.tv_nsec, cycle_high, cycle_low);
|
||||
+ return 1;
|
||||
default:
|
||||
return kvm_psci_call(vcpu);
|
||||
}
|
||||
--
|
||||
2.17.1
|
||||
|
@ -0,0 +1,977 @@
|
||||
From f96a27de35d241a4f5909493ca99847512ec748a Mon Sep 17 00:00:00 2001
|
||||
From: Damon Kwok <damon-kwok@outlook.com>
|
||||
Date: Thu, 24 Jun 2021 14:25:01 +0800
|
||||
Subject: [PATCH] 4.19.x: PTP_KVM support for arm/arm64
|
||||
|
||||
---
|
||||
arch/arm/kernel/setup.c | 2 +
|
||||
arch/arm64/include/asm/kvm_host.h | 2 +
|
||||
arch/arm64/kernel/setup.c | 3 +
|
||||
drivers/clocksource/arm_arch_timer.c | 33 ++++++
|
||||
drivers/firmware/psci.c | 53 +++++++++
|
||||
drivers/ptp/Kconfig | 2 +-
|
||||
drivers/ptp/Makefile | 2 +
|
||||
drivers/ptp/ptp_kvm_arm.c | 28 +++++
|
||||
drivers/ptp/{ptp_kvm.c => ptp_kvm_common.c} | 99 ++++------------
|
||||
drivers/ptp/ptp_kvm_x86.c | 87 ++++++++++++++
|
||||
include/linux/arm-smccc.h | 125 ++++++++++++++++++++
|
||||
include/linux/clocksource.h | 6 +
|
||||
include/linux/clocksource_ids.h | 13 ++
|
||||
include/linux/ptp_kvm.h | 19 +++
|
||||
include/linux/timekeeping.h | 2 +
|
||||
include/uapi/linux/kvm.h | 1 +
|
||||
kernel/time/clocksource.c | 3 +
|
||||
kernel/time/timekeeping.c | 1 +
|
||||
virt/kvm/arm/arm.c | 1 +
|
||||
virt/kvm/arm/psci.c | 80 ++++++++++++-
|
||||
20 files changed, 481 insertions(+), 81 deletions(-)
|
||||
create mode 100644 drivers/ptp/ptp_kvm_arm.c
|
||||
rename drivers/ptp/{ptp_kvm.c => ptp_kvm_common.c} (53%)
|
||||
create mode 100644 drivers/ptp/ptp_kvm_x86.c
|
||||
create mode 100644 include/linux/clocksource_ids.h
|
||||
create mode 100644 include/linux/ptp_kvm.h
|
||||
|
||||
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
|
||||
index 7bbaa293a..137320130 100644
|
||||
--- a/arch/arm/kernel/setup.c
|
||||
+++ b/arch/arm/kernel/setup.c
|
||||
@@ -7,6 +7,7 @@
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
+ #include <linux/arm-smccc.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
@@ -1137,6 +1138,7 @@ void __init setup_arch(char **cmdline_p)
|
||||
|
||||
arm_dt_init_cpu_maps();
|
||||
psci_dt_init();
|
||||
+ kvm_init_hyp_services();
|
||||
#ifdef CONFIG_SMP
|
||||
if (is_smp()) {
|
||||
if (!mdesc->smp_init || !mdesc->smp_init()) {
|
||||
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
|
||||
index 5e720742d..ffc729a43 100644
|
||||
--- a/arch/arm64/include/asm/kvm_host.h
|
||||
+++ b/arch/arm64/include/asm/kvm_host.h
|
||||
@@ -145,6 +145,8 @@ enum vcpu_sysreg {
|
||||
PMSWINC_EL0, /* Software Increment Register */
|
||||
PMUSERENR_EL0, /* User Enable Register */
|
||||
|
||||
+ CNTVOFF_EL2,
|
||||
+
|
||||
/* 32bit specific registers. Keep them at the end of the range */
|
||||
DACR32_EL2, /* Domain Access Control Register */
|
||||
IFSR32_EL2, /* Instruction Fault Status Register */
|
||||
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
|
||||
index b3354ff94..071f26ef4 100644
|
||||
--- a/arch/arm64/kernel/setup.c
|
||||
+++ b/arch/arm64/kernel/setup.c
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
+#include <linux/arm-smccc.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stddef.h>
|
||||
@@ -334,6 +335,8 @@ void __init setup_arch(char **cmdline_p)
|
||||
psci_dt_init();
|
||||
else
|
||||
psci_acpi_init();
|
||||
+
|
||||
+ kvm_init_hyp_services();
|
||||
|
||||
cpu_read_bootcpu_ops();
|
||||
smp_init_cpus();
|
||||
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
|
||||
index 0445ad7e5..b4e8a0a0b 100644
|
||||
--- a/drivers/clocksource/arm_arch_timer.c
|
||||
+++ b/drivers/clocksource/arm_arch_timer.c
|
||||
@@ -19,6 +19,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>
|
||||
@@ -27,6 +28,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>
|
||||
@@ -173,6 +176,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),
|
||||
@@ -1626,3 +1630,32 @@ 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;
|
||||
+ ktime_t ktime;
|
||||
+ u32 ptp_counter;
|
||||
+
|
||||
+ if (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI)
|
||||
+ ptp_counter = ARM_PTP_VIRT_COUNTER;
|
||||
+ else
|
||||
+ ptp_counter = ARM_PTP_PHY_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);
|
||||
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
|
||||
index c80ec1d03..dc958d0b2 100644
|
||||
--- a/drivers/firmware/psci.c
|
||||
+++ b/drivers/firmware/psci.c
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/string.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/linkage.h>
|
||||
@@ -64,6 +66,57 @@ struct psci_operations psci_ops = {
|
||||
.smccc_version = SMCCC_VERSION_1_0,
|
||||
};
|
||||
|
||||
+DECLARE_BITMAP(__kvm_arm_hyp_services, ARM_SMCCC_KVM_NUM_FUNCS) = { };
|
||||
+EXPORT_SYMBOL_GPL(__kvm_arm_hyp_services);
|
||||
+
|
||||
+void __init kvm_init_hyp_services(void)
|
||||
+{
|
||||
+ int i;
|
||||
+ struct arm_smccc_res res;
|
||||
+
|
||||
+ if (psci_ops.smccc_version == ARM_SMCCC_VERSION_1_0)
|
||||
+ 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);
|
||||
+ for (i = 0; i < 32; ++i) {
|
||||
+ if (res.a0 & (i))
|
||||
+ set_bit(i + (32 * 0), __kvm_arm_hyp_services);
|
||||
+ if (res.a1 & (i))
|
||||
+ set_bit(i + (32 * 1), __kvm_arm_hyp_services);
|
||||
+ if (res.a2 & (i))
|
||||
+ set_bit(i + (32 * 2), __kvm_arm_hyp_services);
|
||||
+ if (res.a3 & (i))
|
||||
+ set_bit(i + (32 * 3), __kvm_arm_hyp_services);
|
||||
+ }
|
||||
+
|
||||
+ pr_info("KVM hypervisor services detected (0x%08lx 0x%08lx 0x%08lx 0x%08lx)\n",
|
||||
+ res.a3, res.a2, res.a1, res.a0);
|
||||
+}
|
||||
+
|
||||
+enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
|
||||
+{
|
||||
+ if (psci_ops.smccc_version < SMCCC_VERSION_1_1)
|
||||
+ return SMCCC_CONDUIT_NONE;
|
||||
+
|
||||
+ switch (psci_ops.conduit) {
|
||||
+ case PSCI_CONDUIT_SMC:
|
||||
+ return SMCCC_CONDUIT_SMC;
|
||||
+ case PSCI_CONDUIT_HVC:
|
||||
+ return SMCCC_CONDUIT_HVC;
|
||||
+ default:
|
||||
+ return SMCCC_CONDUIT_NONE;
|
||||
+ }
|
||||
+}
|
||||
+EXPORT_SYMBOL(arm_smccc_1_1_get_conduit);
|
||||
+
|
||||
typedef unsigned long (psci_fn)(unsigned long, unsigned long,
|
||||
unsigned long, unsigned long);
|
||||
static psci_fn *invoke_psci_fn;
|
||||
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
|
||||
index d137c480d..7860ea3cb 100644
|
||||
--- a/drivers/ptp/Kconfig
|
||||
+++ b/drivers/ptp/Kconfig
|
||||
@@ -109,7 +109,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 X86 || ((ARM || ARM64) && ARM_ARCH_TIMER && ARM_PSCI_FW)
|
||||
default y
|
||||
help
|
||||
This driver adds support for using kvm infrastructure as a PTP
|
||||
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
|
||||
index 19efa9cfa..50a6fbd30 100644
|
||||
--- a/drivers/ptp/Makefile
|
||||
+++ b/drivers/ptp/Makefile
|
||||
@@ -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_IXP46X) += ptp_ixp46x.o
|
||||
diff --git a/drivers/ptp/ptp_kvm_arm.c b/drivers/ptp/ptp_kvm_arm.c
|
||||
new file mode 100644
|
||||
index 000000000..b7d28c8df
|
||||
--- /dev/null
|
||||
+++ b/drivers/ptp/ptp_kvm_arm.c
|
||||
@@ -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);
|
||||
+}
|
||||
diff --git a/drivers/ptp/ptp_kvm.c b/drivers/ptp/ptp_kvm_common.c
|
||||
similarity index 53%
|
||||
rename from drivers/ptp/ptp_kvm.c
|
||||
rename to drivers/ptp/ptp_kvm_common.c
|
||||
index c67dd11e0..5c36e2fa0 100644
|
||||
--- a/drivers/ptp/ptp_kvm.c
|
||||
+++ b/drivers/ptp/ptp_kvm_common.c
|
||||
@@ -1,28 +1,18 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Virtual PTP 1588 clock for use with KVM guests
|
||||
*
|
||||
* Copyright (C) 2017 Red Hat Inc.
|
||||
- *
|
||||
- * This program is free software; you can redistribute it and/or modify
|
||||
- * it under the terms of the GNU General Public License as published by
|
||||
- * the Free Software Foundation; either version 2 of the License, or
|
||||
- * (at your option) any later version.
|
||||
- *
|
||||
- * This program is distributed in the hope that it will be useful,
|
||||
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- * GNU General Public License for more details.
|
||||
- *
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#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>
|
||||
@@ -32,58 +22,31 @@ struct kvm_ptp_clock {
|
||||
struct ptp_clock_info caps;
|
||||
};
|
||||
|
||||
-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 DEFINE_SPINLOCK(kvm_ptp_lock);
|
||||
|
||||
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!=0) {
|
||||
+ 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);
|
||||
|
||||
@@ -121,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));
|
||||
@@ -178,19 +136,12 @@ 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) {
|
||||
+ if (ret != -EOPNOTSUPP)
|
||||
+ pr_err("fail to initialize ptp_kvm");
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
kvm_ptp_clock.caps = ptp_kvm_caps;
|
||||
|
||||
diff --git a/drivers/ptp/ptp_kvm_x86.c b/drivers/ptp/ptp_kvm_x86.c
|
||||
new file mode 100644
|
||||
index 000000000..4add42ae3
|
||||
--- /dev/null
|
||||
+++ b/drivers/ptp/ptp_kvm_x86.c
|
||||
@@ -0,0 +1,87 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * Virtual PTP 1588 clock for use with KVM guests
|
||||
+ *
|
||||
+ * Copyright (C) 2017 Red Hat Inc.
|
||||
+ */
|
||||
+
|
||||
+#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>
|
||||
+
|
||||
+phys_addr_t clock_pair_gpa;
|
||||
+struct kvm_clock_pairing clock_pair;
|
||||
+struct pvclock_vsyscall_time_info *hv_clock;
|
||||
+
|
||||
+int kvm_arch_ptp_init(void)
|
||||
+{
|
||||
+ int 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)
|
||||
+ 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)
|
||||
+{
|
||||
+ unsigned long ret;
|
||||
+ unsigned int version;
|
||||
+ int cpu;
|
||||
+ struct pvclock_vcpu_time_info *src;
|
||||
+
|
||||
+ 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);
|
||||
+ 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;
|
||||
+}
|
||||
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
|
||||
index 18863d562..af78262d4 100644
|
||||
--- a/include/linux/arm-smccc.h
|
||||
+++ b/include/linux/arm-smccc.h
|
||||
@@ -1,3 +1,4 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2015, Linaro Limited
|
||||
*
|
||||
@@ -14,6 +15,7 @@
|
||||
#ifndef __LINUX_ARM_SMCCC_H
|
||||
#define __LINUX_ARM_SMCCC_H
|
||||
|
||||
+#include <linux/init.h>
|
||||
#include <uapi/linux/const.h>
|
||||
|
||||
/*
|
||||
@@ -54,11 +56,15 @@
|
||||
#define ARM_SMCCC_OWNER_SIP 2
|
||||
#define ARM_SMCCC_OWNER_OEM 3
|
||||
#define ARM_SMCCC_OWNER_STANDARD 4
|
||||
+#define ARM_SMCCC_OWNER_STANDARD_HYP 5
|
||||
+#define ARM_SMCCC_OWNER_VENDOR_HYP 6
|
||||
#define ARM_SMCCC_OWNER_TRUSTED_APP 48
|
||||
#define ARM_SMCCC_OWNER_TRUSTED_APP_END 49
|
||||
#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 */
|
||||
|
||||
@@ -84,11 +90,67 @@
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
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 ARM_PTP_VIRT_COUNTER 0
|
||||
+#define ARM_PTP_PHY_COUNTER 1
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/types.h>
|
||||
+
|
||||
+enum arm_smccc_conduit {
|
||||
+ SMCCC_CONDUIT_NONE,
|
||||
+ SMCCC_CONDUIT_SMC,
|
||||
+ SMCCC_CONDUIT_HVC,
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * arm_smccc_1_1_get_conduit()
|
||||
+ *
|
||||
+ * Returns the conduit to be used for SMCCCv1.1 or later.
|
||||
+ *
|
||||
+ * When SMCCCv1.1 is not present, returns SMCCC_CONDUIT_NONE.
|
||||
+ */
|
||||
+enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void);
|
||||
+
|
||||
/**
|
||||
* struct arm_smccc_res - Result from SMC/HVC call
|
||||
* @a0-a3 result values from registers 0 to 3
|
||||
@@ -311,5 +373,68 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
|
||||
#define SMCCC_RET_NOT_SUPPORTED -1
|
||||
#define SMCCC_RET_NOT_REQUIRED -2
|
||||
|
||||
+/*
|
||||
+ * Like arm_smccc_1_1* but always returns SMCCC_RET_NOT_SUPPORTED.
|
||||
+ * Used when the SMCCC conduit is not defined. The empty asm statement
|
||||
+ * avoids compiler warnings about unused variables.
|
||||
+ */
|
||||
+#define __fail_smccc_1_1(...) \
|
||||
+ do { \
|
||||
+ __declare_args(__count_args(__VA_ARGS__), __VA_ARGS__); \
|
||||
+ asm ("" __constraints(__count_args(__VA_ARGS__))); \
|
||||
+ if (___res) \
|
||||
+ ___res->a0 = SMCCC_RET_NOT_SUPPORTED; \
|
||||
+ } while (0)
|
||||
+
|
||||
+/*
|
||||
+ * arm_smccc_1_1_invoke() - make an SMCCC v1.1 compliant call
|
||||
+ *
|
||||
+ * This is a variadic macro taking one to eight source arguments, and
|
||||
+ * an optional return structure.
|
||||
+ *
|
||||
+ * @a0-a7: arguments passed in registers 0 to 7
|
||||
+ * @res: result values from registers 0 to 3
|
||||
+ *
|
||||
+ * This macro will make either an HVC call or an SMC call depending on the
|
||||
+ * current SMCCC conduit. If no valid conduit is available then -1
|
||||
+ * (SMCCC_RET_NOT_SUPPORTED) is returned in @res.a0 (if supplied).
|
||||
+ *
|
||||
+ * The return value also provides the conduit that was used.
|
||||
+ */
|
||||
+#define arm_smccc_1_1_invoke(...) ({ \
|
||||
+ int method = arm_smccc_1_1_get_conduit(); \
|
||||
+ switch (method) { \
|
||||
+ case SMCCC_CONDUIT_HVC: \
|
||||
+ arm_smccc_1_1_hvc(__VA_ARGS__); \
|
||||
+ break; \
|
||||
+ case SMCCC_CONDUIT_SMC: \
|
||||
+ arm_smccc_1_1_smc(__VA_ARGS__); \
|
||||
+ break; \
|
||||
+ default: \
|
||||
+ __fail_smccc_1_1(__VA_ARGS__); \
|
||||
+ method = SMCCC_CONDUIT_NONE; \
|
||||
+ break; \
|
||||
+ } \
|
||||
+ method; \
|
||||
+ })
|
||||
+
|
||||
+void __init kvm_init_hyp_services(void);
|
||||
+
|
||||
+/*
|
||||
+ * This helper will be called in guest. We put it here then both arm and arm64
|
||||
+ * guest can touch it.
|
||||
+ */
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/err.h>
|
||||
+static inline bool kvm_arm_hyp_service_available(u32 func_id)
|
||||
+{
|
||||
+ extern DECLARE_BITMAP(__kvm_arm_hyp_services, ARM_SMCCC_KVM_NUM_FUNCS);
|
||||
+
|
||||
+ if (func_id >= ARM_SMCCC_KVM_NUM_FUNCS)
|
||||
+ return false;
|
||||
+
|
||||
+ return test_bit(func_id, __kvm_arm_hyp_services);
|
||||
+}
|
||||
+
|
||||
#endif /*__ASSEMBLY__*/
|
||||
#endif /*__LINUX_ARM_SMCCC_H*/
|
||||
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
|
||||
index 308918928..043967d3f 100644
|
||||
--- a/include/linux/clocksource.h
|
||||
+++ b/include/linux/clocksource.h
|
||||
@@ -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>
|
||||
|
||||
@@ -49,6 +50,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.
|
||||
* @read: returns a cycle value, passes clocksource as argument
|
||||
* @enable: optional function to enable the clocksource
|
||||
* @disable: optional function to disable the clocksource
|
||||
@@ -91,6 +96,7 @@ struct clocksource {
|
||||
const char *name;
|
||||
struct list_head list;
|
||||
int rating;
|
||||
+ enum clocksource_ids id;
|
||||
int (*enable)(struct clocksource *cs);
|
||||
void (*disable)(struct clocksource *cs);
|
||||
unsigned long flags;
|
||||
diff --git a/include/linux/clocksource_ids.h b/include/linux/clocksource_ids.h
|
||||
new file mode 100644
|
||||
index 000000000..93bec8426
|
||||
--- /dev/null
|
||||
+++ b/include/linux/clocksource_ids.h
|
||||
@@ -0,0 +1,13 @@
|
||||
+/* 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
|
||||
+
|
||||
diff --git a/include/linux/ptp_kvm.h b/include/linux/ptp_kvm.h
|
||||
new file mode 100644
|
||||
index 000000000..f960a719f
|
||||
--- /dev/null
|
||||
+++ b/include/linux/ptp_kvm.h
|
||||
@@ -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_ */
|
||||
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
|
||||
index a5a3cfc3c..24808479f 100644
|
||||
--- a/include/linux/timekeeping.h
|
||||
+++ b/include/linux/timekeeping.h
|
||||
@@ -2,6 +2,7 @@
|
||||
#ifndef _LINUX_TIMEKEEPING_H
|
||||
#define _LINUX_TIMEKEEPING_H
|
||||
|
||||
+#include <linux/clocksource_ids.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
/* Included from linux/ktime.h */
|
||||
@@ -207,6 +208,7 @@ struct system_time_snapshot {
|
||||
u64 cycles;
|
||||
ktime_t real;
|
||||
ktime_t raw;
|
||||
+ enum clocksource_ids cs_id;
|
||||
unsigned int clock_was_set_seq;
|
||||
u8 cs_was_changed_seq;
|
||||
};
|
||||
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
|
||||
index c297abc4e..e5962ad27 100644
|
||||
--- a/include/uapi/linux/kvm.h
|
||||
+++ b/include/uapi/linux/kvm.h
|
||||
@@ -956,6 +956,7 @@ struct kvm_ppc_resize_hpt {
|
||||
#define KVM_CAP_NESTED_STATE 157
|
||||
#define KVM_CAP_ARM_INJECT_SERROR_ESR 158
|
||||
#define KVM_CAP_MSR_PLATFORM_INFO 159
|
||||
+#define KVM_CAP_ARM_KVM_PTP 160
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
|
||||
index f80bb104c..6b3a072a1 100644
|
||||
--- a/kernel/time/clocksource.c
|
||||
+++ b/kernel/time/clocksource.c
|
||||
@@ -944,6 +944,9 @@ int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
+ if (WARN_ON_ONCE((unsigned int)cs->id >= CSID_MAX))
|
||||
+ cs->id = CSID_GENERIC;
|
||||
+
|
||||
/* Initialize mult/shift and max_idle_ns */
|
||||
__clocksource_update_freq_scale(cs, scale, freq);
|
||||
|
||||
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
|
||||
index c66fd11d9..348ea7de1 100644
|
||||
--- a/kernel/time/timekeeping.c
|
||||
+++ b/kernel/time/timekeeping.c
|
||||
@@ -978,6 +978,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,
|
||||
diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
|
||||
index d982650de..716b9c7e8 100644
|
||||
--- a/virt/kvm/arm/arm.c
|
||||
+++ b/virt/kvm/arm/arm.c
|
||||
@@ -212,6 +212,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
case KVM_CAP_READONLY_MEM:
|
||||
case KVM_CAP_MP_STATE:
|
||||
case KVM_CAP_IMMEDIATE_EXIT:
|
||||
+ case KVM_CAP_ARM_KVM_PTP:
|
||||
r = 1;
|
||||
break;
|
||||
case KVM_CAP_ARM_SET_DEVICE_ADDR:
|
||||
diff --git a/virt/kvm/arm/psci.c b/virt/kvm/arm/psci.c
|
||||
index 34d08ee63..0f35819ba 100644
|
||||
--- a/virt/kvm/arm/psci.c
|
||||
+++ b/virt/kvm/arm/psci.c
|
||||
@@ -398,22 +398,65 @@ static int kvm_psci_call(struct kvm_vcpu *vcpu)
|
||||
};
|
||||
}
|
||||
|
||||
+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 in the same
|
||||
+ * time to keep consistency and precision.
|
||||
+ */
|
||||
+ ktime_get_snapshot(&systime_snapshot);
|
||||
+
|
||||
+ // binding ptp_kvm clocksource to arm_arch_counter
|
||||
+ if (systime_snapshot.cs_id != CSID_ARM_ARCH_COUNTER)
|
||||
+ return;
|
||||
+
|
||||
+ val[0] = upper_32_bits(systime_snapshot.real);
|
||||
+ val[1] = lower_32_bits(systime_snapshot.real);
|
||||
+
|
||||
+ /*
|
||||
+ * which of virtual counter or physical counter being
|
||||
+ * asked for is decided by the r1 value of SMCCC
|
||||
+ * call. If no invalid r1 value offered, default cycle
|
||||
+ * value(-1) will be returned.
|
||||
+ * Note: keep in mind that feature is u32 and smccc_get_arg1
|
||||
+ * will return u64, so need auto cast here.
|
||||
+ */
|
||||
+ feature = smccc_get_arg1(vcpu);
|
||||
+ switch (feature) {
|
||||
+ case ARM_PTP_VIRT_COUNTER:
|
||||
+ cycles = systime_snapshot.cycles - vcpu_read_sys_reg(vcpu, CNTVOFF_EL2);
|
||||
+ break;
|
||||
+ case ARM_PTP_PHY_COUNTER:
|
||||
+ cycles = systime_snapshot.cycles;
|
||||
+ break;
|
||||
+ default:
|
||||
+ val[0] = SMCCC_RET_NOT_SUPPORTED;
|
||||
+ break;
|
||||
+ }
|
||||
+ 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);
|
||||
- u32 val = SMCCC_RET_NOT_SUPPORTED;
|
||||
+ u64 val[4] = {SMCCC_RET_NOT_SUPPORTED};
|
||||
u32 feature;
|
||||
|
||||
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);
|
||||
switch(feature) {
|
||||
case ARM_SMCCC_ARCH_WORKAROUND_1:
|
||||
if (kvm_arm_harden_branch_predictor())
|
||||
- val = SMCCC_RET_SUCCESS;
|
||||
+ val[0] = SMCCC_RET_SUCCESS;
|
||||
break;
|
||||
case ARM_SMCCC_ARCH_WORKAROUND_2:
|
||||
switch (kvm_arm_have_ssbd()) {
|
||||
@@ -421,21 +464,46 @@ int kvm_hvc_call_handler(struct kvm_vcpu *vcpu)
|
||||
case KVM_SSBD_UNKNOWN:
|
||||
break;
|
||||
case KVM_SSBD_KERNEL:
|
||||
- val = SMCCC_RET_SUCCESS;
|
||||
+ val[0] = SMCCC_RET_SUCCESS;
|
||||
break;
|
||||
case KVM_SSBD_FORCE_ENABLE:
|
||||
case KVM_SSBD_MITIGATED:
|
||||
- val = SMCCC_RET_NOT_REQUIRED;
|
||||
+ val[0] = SMCCC_RET_NOT_REQUIRED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
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;
|
||||
+ /*
|
||||
+ * This serves virtual kvm_ptp.
|
||||
+ * Four values will be passed back.
|
||||
+ * reg0 stores high 32-bits of host ktime;
|
||||
+ * reg1 stores low 32-bits of host ktime;
|
||||
+ * For ARM_PTP_VIRT_COUNTER:
|
||||
+ * reg2 stores high 32-bits of difference of host cycles and cntvoff;
|
||||
+ * reg3 stores low 32-bits of difference of host cycles and cntvoff.
|
||||
+ * For ARM_PTP_PHY_COUNTER:
|
||||
+ * reg2 stores the high 32-bits of host cycles;
|
||||
+ * reg3 stores the low 32-bits of host cycles.
|
||||
+ */
|
||||
+ case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
|
||||
+ kvm_ptp_get_time(vcpu, val);
|
||||
+ break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
||||
--
|
||||
2.32.0
|
||||
|
@ -0,0 +1,974 @@
|
||||
From 772e627bb2aa29a6d006f08bdf7bc1492671d1fe Mon Sep 17 00:00:00 2001
|
||||
From: Damon Kwok <damon-kwok@outlook.com>
|
||||
Date: Thu, 24 Jun 2021 15:04:19 +0800
|
||||
Subject: [PATCH] 5.10.x: PTP_KVM support for arm/arm64
|
||||
|
||||
---
|
||||
Documentation/virt/kvm/api.rst | 9 ++
|
||||
Documentation/virt/kvm/arm/index.rst | 1 +
|
||||
Documentation/virt/kvm/arm/ptp_kvm.rst | 31 +++++++
|
||||
Documentation/virt/kvm/timekeeping.rst | 35 ++++++++
|
||||
arch/arm/kernel/setup.c | 5 ++
|
||||
arch/arm64/kernel/setup.c | 1 +
|
||||
arch/arm64/kvm/hypercalls.c | 86 ++++++++++++++++--
|
||||
arch/arm64/kvm/reset.c | 1 +
|
||||
drivers/clocksource/arm_arch_timer.c | 33 +++++++
|
||||
drivers/firmware/smccc/smccc.c | 37 ++++++++
|
||||
drivers/ptp/Kconfig | 2 +-
|
||||
drivers/ptp/Makefile | 2 +
|
||||
drivers/ptp/ptp_kvm_arm.c | 28 ++++++
|
||||
drivers/ptp/{ptp_kvm.c => ptp_kvm_common.c} | 85 +++++-------------
|
||||
drivers/ptp/ptp_kvm_x86.c | 96 +++++++++++++++++++++
|
||||
include/linux/arm-smccc.h | 59 +++++++++++++
|
||||
include/linux/clocksource.h | 6 ++
|
||||
include/linux/clocksource_ids.h | 12 +++
|
||||
include/linux/ptp_kvm.h | 19 ++++
|
||||
include/linux/timekeeping.h | 12 +--
|
||||
include/uapi/linux/kvm.h | 1 +
|
||||
kernel/time/clocksource.c | 2 +
|
||||
kernel/time/timekeeping.c | 1 +
|
||||
23 files changed, 487 insertions(+), 77 deletions(-)
|
||||
create mode 100644 Documentation/virt/kvm/arm/ptp_kvm.rst
|
||||
create mode 100644 drivers/ptp/ptp_kvm_arm.c
|
||||
rename drivers/ptp/{ptp_kvm.c => ptp_kvm_common.c} (60%)
|
||||
create mode 100644 drivers/ptp/ptp_kvm_x86.c
|
||||
create mode 100644 include/linux/clocksource_ids.h
|
||||
create mode 100644 include/linux/ptp_kvm.h
|
||||
|
||||
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
|
||||
index cd8a585..4b97c22 100644
|
||||
--- a/Documentation/virt/kvm/api.rst
|
||||
+++ b/Documentation/virt/kvm/api.rst
|
||||
@@ -6398,3 +6398,12 @@ When enabled, KVM will disable paravirtual features provided to the
|
||||
guest according to the bits in the KVM_CPUID_FEATURES CPUID leaf
|
||||
(0x40000001). Otherwise, a guest may use the paravirtual features
|
||||
regardless of what has actually been exposed through the CPUID leaf.
|
||||
+
|
||||
+8.27 KVM_CAP_PTP_KVM
|
||||
+--------------------
|
||||
+
|
||||
+:Architectures: arm64
|
||||
+
|
||||
+This capability indicates that KVM virtual PTP service is supported in host.
|
||||
+It must company with the implementation of KVM virtual PTP service in host
|
||||
+so VMM can probe if there is the service in host by checking this capability.
|
||||
diff --git a/Documentation/virt/kvm/arm/index.rst b/Documentation/virt/kvm/arm/index.rst
|
||||
index 3e2b2ab..78a9b67 100644
|
||||
--- a/Documentation/virt/kvm/arm/index.rst
|
||||
+++ b/Documentation/virt/kvm/arm/index.rst
|
||||
@@ -10,3 +10,4 @@ ARM
|
||||
hyp-abi
|
||||
psci
|
||||
pvtime
|
||||
+ ptp_kvm
|
||||
diff --git a/Documentation/virt/kvm/arm/ptp_kvm.rst b/Documentation/virt/kvm/arm/ptp_kvm.rst
|
||||
new file mode 100644
|
||||
index 0000000..7be7e65
|
||||
--- /dev/null
|
||||
+++ b/Documentation/virt/kvm/arm/ptp_kvm.rst
|
||||
@@ -0,0 +1,31 @@
|
||||
+.. SPDX-License-Identifier: GPL-2.0
|
||||
+
|
||||
+PTP_KVM support for arm/arm64
|
||||
+=============================
|
||||
+
|
||||
+PTP_KVM is used for time sync between guest and host in a high precision.
|
||||
+It needs to get the wall time and counter value from the host and transfer these
|
||||
+to guest via hypercall service. So one more hypercall service has been added.
|
||||
+
|
||||
+This new SMCCC hypercall is defined as:
|
||||
+
|
||||
+* ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID: 0x86000001
|
||||
+
|
||||
+As both 32 and 64-bits ptp_kvm client should be supported, we choose SMC32/HVC32
|
||||
+calling convention.
|
||||
+
|
||||
+ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
|
||||
+
|
||||
+ ============= ========== ==========
|
||||
+ Function ID: (uint32) 0x86000001
|
||||
+ Arguments: (uint32) ARM_PTP_PHY_COUNTER(1) or ARM_PTP_VIRT_COUNTER(0)
|
||||
+ which indicate acquiring physical counter or
|
||||
+ virtual counter respectively.
|
||||
+ Return Value: val0(uint32) NOT_SUPPORTED(-1) or upper 32 bits of wall clock time(64-bits).
|
||||
+ val1(uint32) Lower 32 bits of wall clock time.
|
||||
+ val2(uint32) Upper 32 bits of counter cycle(64-bits).
|
||||
+ val3(uint32) Lower 32 bits of counter cycle.
|
||||
+ Endianness: No Restrictions.
|
||||
+ ============= ========== ==========
|
||||
+
|
||||
+More info see section 5 in Documentation/virt/kvm/timekeeping.rst.
|
||||
diff --git a/Documentation/virt/kvm/timekeeping.rst b/Documentation/virt/kvm/timekeeping.rst
|
||||
index 21ae7ef..c81383e 100644
|
||||
--- a/Documentation/virt/kvm/timekeeping.rst
|
||||
+++ b/Documentation/virt/kvm/timekeeping.rst
|
||||
@@ -13,6 +13,7 @@ Timekeeping Virtualization for X86-Based Architectures
|
||||
2) Timing Devices
|
||||
3) TSC Hardware
|
||||
4) Virtualization Problems
|
||||
+ 5) KVM virtual PTP clock
|
||||
|
||||
1. Overview
|
||||
===========
|
||||
@@ -643,3 +644,37 @@ by using CPU utilization itself as a signalling channel. Preventing such
|
||||
problems would require completely isolated virtual time which may not track
|
||||
real time any longer. This may be useful in certain security or QA contexts,
|
||||
but in general isn't recommended for real-world deployment scenarios.
|
||||
+
|
||||
+5. KVM virtual PTP clock
|
||||
+========================
|
||||
+
|
||||
+NTP (Network Time Protocol) is often used to sync time in a VM. Unfortunately,
|
||||
+the precision of NTP is limited due to unknown delays in the network.
|
||||
+
|
||||
+KVM virtual PTP clock (PTP_KVM) offers another way to sync time in VM; use the
|
||||
+host's clock rather than one from a remote machine. Having a synchronization
|
||||
+mechanism for the virtualization environment allows us to keep all the guests
|
||||
+running on the same host in sync.
|
||||
+In general, the delay of communication between host and guest is quite
|
||||
+small, so ptp_kvm can offer time sync precision up to in order of nanoseconds.
|
||||
+Please keep in mind that ptp_kvm just limits itself to be a channel which
|
||||
+transmits the remote clock from host to guest. An application, eg. chrony, is
|
||||
+needed in usersapce of VM in order to set the guest time.
|
||||
+
|
||||
+After ptp_kvm is initialized, there will be a new device node under /dev called
|
||||
+ptp%d. A guest userspace service, like chrony, can use this device to get host
|
||||
+walltime, sometimes also counter cycle, which depends on the service it calls.
|
||||
+Then this guest userspace service can use those data to do the time sync for
|
||||
+the guest.
|
||||
+The following is the work flow of ptp_kvm:
|
||||
+
|
||||
+a) time sync service in guest userspace call ioctl on ptp device /dev/ptp%d.
|
||||
+b) ptp_kvm module in guest receives this request then invokes hypercall to
|
||||
+ route into host kernel to request host's walltime/counter cycle.
|
||||
+c) ptp_kvm hypercall service on the host responds to the request and sends data
|
||||
+ back.
|
||||
+d) ptp in guest copies the data to userspace.
|
||||
+
|
||||
+ptp_kvm consists of components running on the guest and host. Step 2 consists of
|
||||
+a guest driver making a hypercall whilst step 3 involves the hypervisor responding
|
||||
+with information.
|
||||
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
|
||||
index f90479d..472daea 100644
|
||||
--- a/arch/arm/kernel/setup.c
|
||||
+++ b/arch/arm/kernel/setup.c
|
||||
@@ -1154,6 +1154,11 @@ void __init setup_arch(char **cmdline_p)
|
||||
|
||||
arm_dt_init_cpu_maps();
|
||||
psci_dt_init();
|
||||
+
|
||||
+#ifdef CONFIG_HAVE_ARM_SMCCC_DISCOVERY
|
||||
+ kvm_init_hyp_services();
|
||||
+#endif
|
||||
+
|
||||
#ifdef CONFIG_SMP
|
||||
if (is_smp()) {
|
||||
if (!mdesc->smp_init || !mdesc->smp_init()) {
|
||||
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
|
||||
index 133257f..d1dbe41 100644
|
||||
--- a/arch/arm64/kernel/setup.c
|
||||
+++ b/arch/arm64/kernel/setup.c
|
||||
@@ -353,6 +353,7 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
|
||||
else
|
||||
psci_acpi_init();
|
||||
|
||||
+ kvm_init_hyp_services();
|
||||
init_bootcpu_ops();
|
||||
smp_init_cpus();
|
||||
smp_build_mpidr_hash();
|
||||
diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c
|
||||
index 25ea4ec..9a48345 100644
|
||||
--- a/arch/arm64/kvm/hypercalls.c
|
||||
+++ b/arch/arm64/kvm/hypercalls.c
|
||||
@@ -9,16 +9,59 @@
|
||||
#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 in the same
|
||||
+ * time to keep consistency and precision.
|
||||
+ */
|
||||
+ ktime_get_snapshot(&systime_snapshot);
|
||||
+
|
||||
+ // binding ptp_kvm clocksource to arm_arch_counter
|
||||
+ if (systime_snapshot.cs_id != CSID_ARM_ARCH_COUNTER)
|
||||
+ return;
|
||||
+
|
||||
+ val[0] = upper_32_bits(systime_snapshot.real);
|
||||
+ val[1] = lower_32_bits(systime_snapshot.real);
|
||||
+
|
||||
+ /*
|
||||
+ * which of virtual counter or physical counter being
|
||||
+ * asked for is decided by the r1 value of SMCCC
|
||||
+ * call. If no invalid r1 value offered, default cycle
|
||||
+ * value(-1) will be returned.
|
||||
+ * Note: keep in mind that feature is u32 and smccc_get_arg1
|
||||
+ * will return u64, so need auto cast here.
|
||||
+ */
|
||||
+ feature = smccc_get_arg1(vcpu);
|
||||
+ switch (feature) {
|
||||
+ case ARM_PTP_VIRT_COUNTER:
|
||||
+ cycles = systime_snapshot.cycles - vcpu_read_sys_reg(vcpu, CNTVOFF_EL2);
|
||||
+ break;
|
||||
+ case ARM_PTP_PHY_COUNTER:
|
||||
+ cycles = systime_snapshot.cycles;
|
||||
+ break;
|
||||
+ default:
|
||||
+ val[0] = SMCCC_RET_NOT_SUPPORTED;
|
||||
+ break;
|
||||
+ }
|
||||
+ 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 +71,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,27 +97,52 @@ 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;
|
||||
+ /*
|
||||
+ * This serves virtual kvm_ptp.
|
||||
+ * Four values will be passed back.
|
||||
+ * reg0 stores high 32-bits of host ktime;
|
||||
+ * reg1 stores low 32-bits of host ktime;
|
||||
+ * For ARM_PTP_VIRT_COUNTER:
|
||||
+ * reg2 stores high 32-bits of difference of host cycles and cntvoff;
|
||||
+ * reg3 stores low 32-bits of difference of host cycles and cntvoff.
|
||||
+ * For ARM_PTP_PHY_COUNTER:
|
||||
+ * reg2 stores the high 32-bits of host cycles;
|
||||
+ * reg3 stores the low 32-bits of host cycles.
|
||||
+ */
|
||||
+ case ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID:
|
||||
+ kvm_ptp_get_time(vcpu, val);
|
||||
break;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
|
||||
index b969c21..77fae02 100644
|
||||
--- a/arch/arm64/kvm/reset.c
|
||||
+++ b/arch/arm64/kvm/reset.c
|
||||
@@ -75,6 +75,7 @@ int kvm_arch_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
break;
|
||||
case KVM_CAP_SET_GUEST_DEBUG:
|
||||
case KVM_CAP_VCPU_ATTRIBUTES:
|
||||
+ case KVM_CAP_PTP_KVM:
|
||||
r = 1;
|
||||
break;
|
||||
case KVM_CAP_ARM_VM_IPA_SIZE:
|
||||
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
|
||||
index d017782..7cbb9fa 100644
|
||||
--- a/drivers/clocksource/arm_arch_timer.c
|
||||
+++ b/drivers/clocksource/arm_arch_timer.c
|
||||
@@ -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,32 @@ 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;
|
||||
+ ktime_t ktime;
|
||||
+ u32 ptp_counter;
|
||||
+
|
||||
+ if (arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI)
|
||||
+ ptp_counter = ARM_PTP_VIRT_COUNTER;
|
||||
+ else
|
||||
+ ptp_counter = ARM_PTP_PHY_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);
|
||||
diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c
|
||||
index 00c88b8..e153c71 100644
|
||||
--- a/drivers/firmware/smccc/smccc.c
|
||||
+++ b/drivers/firmware/smccc/smccc.c
|
||||
@@ -7,10 +7,47 @@
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/string.h>
|
||||
|
||||
static u32 smccc_version = ARM_SMCCC_VERSION_1_0;
|
||||
static enum arm_smccc_conduit smccc_conduit = SMCCC_CONDUIT_NONE;
|
||||
|
||||
+DECLARE_BITMAP(__kvm_arm_hyp_services, ARM_SMCCC_KVM_NUM_FUNCS) = { };
|
||||
+EXPORT_SYMBOL_GPL(__kvm_arm_hyp_services);
|
||||
+
|
||||
+void __init kvm_init_hyp_services(void)
|
||||
+{
|
||||
+ int i;
|
||||
+ struct arm_smccc_res res;
|
||||
+
|
||||
+ if (arm_smccc_get_version() == ARM_SMCCC_VERSION_1_0)
|
||||
+ 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);
|
||||
+ for (i = 0; i < 32; ++i) {
|
||||
+ if (res.a0 & (i))
|
||||
+ set_bit(i + (32 * 0), __kvm_arm_hyp_services);
|
||||
+ if (res.a1 & (i))
|
||||
+ set_bit(i + (32 * 1), __kvm_arm_hyp_services);
|
||||
+ if (res.a2 & (i))
|
||||
+ set_bit(i + (32 * 2), __kvm_arm_hyp_services);
|
||||
+ if (res.a3 & (i))
|
||||
+ set_bit(i + (32 * 3), __kvm_arm_hyp_services);
|
||||
+ }
|
||||
+
|
||||
+ pr_info("KVM hypervisor services detected (0x%08lx 0x%08lx 0x%08lx 0x%08lx)\n",
|
||||
+ res.a3, res.a2, res.a1, res.a0);
|
||||
+}
|
||||
+
|
||||
void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit)
|
||||
{
|
||||
smccc_version = version;
|
||||
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
|
||||
index deb429a..5523f96 100644
|
||||
--- a/drivers/ptp/Kconfig
|
||||
+++ b/drivers/ptp/Kconfig
|
||||
@@ -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 X86 || (HAVE_ARM_SMCCC_DISCOVERY && ARM_ARCH_TIMER)
|
||||
default y
|
||||
help
|
||||
This driver adds support for using kvm infrastructure as a PTP
|
||||
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
|
||||
index 7aff75f..9fa5ede 100644
|
||||
--- a/drivers/ptp/Makefile
|
||||
+++ b/drivers/ptp/Makefile
|
||||
@@ -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
|
||||
diff --git a/drivers/ptp/ptp_kvm_arm.c b/drivers/ptp/ptp_kvm_arm.c
|
||||
new file mode 100644
|
||||
index 0000000..b7d28c8
|
||||
--- /dev/null
|
||||
+++ b/drivers/ptp/ptp_kvm_arm.c
|
||||
@@ -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);
|
||||
+}
|
||||
diff --git a/drivers/ptp/ptp_kvm.c b/drivers/ptp/ptp_kvm_common.c
|
||||
similarity index 60%
|
||||
rename from drivers/ptp/ptp_kvm.c
|
||||
rename to drivers/ptp/ptp_kvm_common.c
|
||||
index 658d33f..5c36e2f 100644
|
||||
--- a/drivers/ptp/ptp_kvm.c
|
||||
+++ b/drivers/ptp/ptp_kvm_common.c
|
||||
@@ -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!=0) {
|
||||
+ 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,12 @@ 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) {
|
||||
+ if (ret != -EOPNOTSUPP)
|
||||
+ pr_err("fail to initialize ptp_kvm");
|
||||
+ return ret;
|
||||
+ }
|
||||
|
||||
kvm_ptp_clock.caps = ptp_kvm_caps;
|
||||
|
||||
diff --git a/drivers/ptp/ptp_kvm_x86.c b/drivers/ptp/ptp_kvm_x86.c
|
||||
new file mode 100644
|
||||
index 0000000..e011d69
|
||||
--- /dev/null
|
||||
+++ b/drivers/ptp/ptp_kvm_x86.c
|
||||
@@ -0,0 +1,96 @@
|
||||
+// 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>
|
||||
+
|
||||
+phys_addr_t clock_pair_gpa;
|
||||
+struct kvm_clock_pairing clock_pair;
|
||||
+struct pvclock_vsyscall_time_info *hv_clock;
|
||||
+
|
||||
+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)
|
||||
+{
|
||||
+ unsigned 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)
|
||||
+{
|
||||
+ unsigned long ret;
|
||||
+ unsigned int version;
|
||||
+ int cpu;
|
||||
+ struct pvclock_vcpu_time_info *src;
|
||||
+
|
||||
+ 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;
|
||||
+}
|
||||
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
|
||||
index f860645..92b46cb 100644
|
||||
--- a/include/linux/arm-smccc.h
|
||||
+++ b/include/linux/arm-smccc.h
|
||||
@@ -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 ARM_PTP_VIRT_COUNTER 0
|
||||
+#define ARM_PTP_PHY_COUNTER 1
|
||||
+
|
||||
/* Paravirtualised time calls (defined by ARM DEN0057A) */
|
||||
#define ARM_SMCCC_HV_PV_TIME_FEATURES \
|
||||
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
|
||||
@@ -391,5 +432,23 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
|
||||
method; \
|
||||
})
|
||||
|
||||
+void __init kvm_init_hyp_services(void);
|
||||
+
|
||||
+/*
|
||||
+ * This helper will be called in guest. We put it here then both arm and arm64
|
||||
+ * guest can touch it.
|
||||
+ */
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/err.h>
|
||||
+static inline bool kvm_arm_hyp_service_available(u32 func_id)
|
||||
+{
|
||||
+ extern DECLARE_BITMAP(__kvm_arm_hyp_services, ARM_SMCCC_KVM_NUM_FUNCS);
|
||||
+
|
||||
+ if (func_id >= ARM_SMCCC_KVM_NUM_FUNCS)
|
||||
+ return false;
|
||||
+
|
||||
+ return test_bit(func_id, __kvm_arm_hyp_services);
|
||||
+}
|
||||
+
|
||||
#endif /*__ASSEMBLY__*/
|
||||
#endif /*__LINUX_ARM_SMCCC_H*/
|
||||
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
|
||||
index 86d143d..1290d0d 100644
|
||||
--- a/include/linux/clocksource.h
|
||||
+++ b/include/linux/clocksource.h
|
||||
@@ -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;
|
||||
|
||||
diff --git a/include/linux/clocksource_ids.h b/include/linux/clocksource_ids.h
|
||||
new file mode 100644
|
||||
index 0000000..16775d7
|
||||
--- /dev/null
|
||||
+++ b/include/linux/clocksource_ids.h
|
||||
@@ -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
|
||||
diff --git a/include/linux/ptp_kvm.h b/include/linux/ptp_kvm.h
|
||||
new file mode 100644
|
||||
index 0000000..f960a71
|
||||
--- /dev/null
|
||||
+++ b/include/linux/ptp_kvm.h
|
||||
@@ -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_ */
|
||||
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
|
||||
index 7f7e4a3..2ee0535 100644
|
||||
--- a/include/linux/timekeeping.h
|
||||
+++ b/include/linux/timekeeping.h
|
||||
@@ -3,6 +3,7 @@
|
||||
#define _LINUX_TIMEKEEPING_H
|
||||
|
||||
#include <linux/errno.h>
|
||||
+#include <linux/clocksource_ids.h>
|
||||
|
||||
/* Included from linux/ktime.h */
|
||||
|
||||
@@ -244,11 +245,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;
|
||||
};
|
||||
|
||||
/**
|
||||
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
|
||||
index ca41220..797c40b 100644
|
||||
--- a/include/uapi/linux/kvm.h
|
||||
+++ b/include/uapi/linux/kvm.h
|
||||
@@ -1053,6 +1053,7 @@ struct kvm_ppc_resize_hpt {
|
||||
#define KVM_CAP_X86_USER_SPACE_MSR 188
|
||||
#define KVM_CAP_X86_MSR_FILTER 189
|
||||
#define KVM_CAP_ENFORCE_PV_FEATURE_CPUID 190
|
||||
+#define KVM_CAP_PTP_KVM 191
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
|
||||
index 02441ea..6b38d49 100644
|
||||
--- a/kernel/time/clocksource.c
|
||||
+++ b/kernel/time/clocksource.c
|
||||
@@ -928,6 +928,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",
|
||||
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
|
||||
index 6858a31..eb04a2d 100644
|
||||
--- a/kernel/time/timekeeping.c
|
||||
+++ b/kernel/time/timekeeping.c
|
||||
@@ -1053,6 +1053,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,
|
||||
--
|
||||
2.32.0
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,81 +0,0 @@
|
||||
From 3d1d7f8922ed2f080f6d8e08df0d51e22f9590ec Mon Sep 17 00:00:00 2001
|
||||
From: Jianyong Wu <jianyong.wu@arm.com>
|
||||
Date: Wed, 1 Apr 2020 15:19:29 +0800
|
||||
Subject: [PATCH 1/9] arm/arm64: Provide a wrapper for SMCCC 1.1 calls
|
||||
|
||||
From: Steven Price <steven.price@arm.com>
|
||||
|
||||
SMCCC 1.1 calls may use either HVC or SMC depending on the PSCI
|
||||
conduit. Rather than coding this in every call site, provide a macro
|
||||
which uses the correct instruction. The macro also handles the case
|
||||
where no conduit is configured/available returning a not supported error
|
||||
in res, along with returning the conduit used for the call.
|
||||
|
||||
This allow us to remove some duplicated code and will be useful later
|
||||
when adding paravirtualized time hypervisor calls.
|
||||
|
||||
Signed-off-by: Steven Price <steven.price@arm.com>
|
||||
Acked-by: Will Deacon <will@kernel.org>
|
||||
Signed-off-by: Marc Zyngier <maz@kernel.org>
|
||||
---
|
||||
include/linux/arm-smccc.h | 45 +++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 45 insertions(+)
|
||||
|
||||
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
|
||||
index 080012a6f025..131edde5d37e 100644
|
||||
--- a/include/linux/arm-smccc.h
|
||||
+++ b/include/linux/arm-smccc.h
|
||||
@@ -302,5 +302,50 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1,
|
||||
#define SMCCC_RET_NOT_SUPPORTED -1
|
||||
#define SMCCC_RET_NOT_REQUIRED -2
|
||||
|
||||
+/*
|
||||
+ * Like arm_smccc_1_1* but always returns SMCCC_RET_NOT_SUPPORTED.
|
||||
+ * Used when the SMCCC conduit is not defined. The empty asm statement
|
||||
+ * avoids compiler warnings about unused variables.
|
||||
+ */
|
||||
+#define __fail_smccc_1_1(...) \
|
||||
+ do { \
|
||||
+ __declare_args(__count_args(__VA_ARGS__), __VA_ARGS__); \
|
||||
+ asm ("" __constraints(__count_args(__VA_ARGS__))); \
|
||||
+ if (___res) \
|
||||
+ ___res->a0 = SMCCC_RET_NOT_SUPPORTED; \
|
||||
+ } while (0)
|
||||
+
|
||||
+/*
|
||||
+ * arm_smccc_1_1_invoke() - make an SMCCC v1.1 compliant call
|
||||
+ *
|
||||
+ * This is a variadic macro taking one to eight source arguments, and
|
||||
+ * an optional return structure.
|
||||
+ *
|
||||
+ * @a0-a7: arguments passed in registers 0 to 7
|
||||
+ * @res: result values from registers 0 to 3
|
||||
+ *
|
||||
+ * This macro will make either an HVC call or an SMC call depending on the
|
||||
+ * current SMCCC conduit. If no valid conduit is available then -1
|
||||
+ * (SMCCC_RET_NOT_SUPPORTED) is returned in @res.a0 (if supplied).
|
||||
+ *
|
||||
+ * The return value also provides the conduit that was used.
|
||||
+ */
|
||||
+#define arm_smccc_1_1_invoke(...) ({ \
|
||||
+ int method = arm_smccc_1_1_get_conduit(); \
|
||||
+ switch (method) { \
|
||||
+ case SMCCC_CONDUIT_HVC: \
|
||||
+ arm_smccc_1_1_hvc(__VA_ARGS__); \
|
||||
+ break; \
|
||||
+ case SMCCC_CONDUIT_SMC: \
|
||||
+ arm_smccc_1_1_smc(__VA_ARGS__); \
|
||||
+ break; \
|
||||
+ default: \
|
||||
+ __fail_smccc_1_1(__VA_ARGS__); \
|
||||
+ method = SMCCC_CONDUIT_NONE; \
|
||||
+ break; \
|
||||
+ } \
|
||||
+ method; \
|
||||
+ })
|
||||
+
|
||||
#endif /*__ASSEMBLY__*/
|
||||
#endif /*__LINUX_ARM_SMCCC_H*/
|
||||
--
|
||||
2.17.1
|
||||
|
@ -1,81 +0,0 @@
|
||||
From b830806f5cd02119be9b25812b3ea56d97cd08f3 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Rutland <mark.rutland@arm.com>
|
||||
Date: Fri, 9 Aug 2019 14:22:40 +0100
|
||||
Subject: [PATCH 2/9] arm/arm64: smccc/psci: add arm_smccc_1_1_get_conduit()
|
||||
|
||||
SMCCC callers are currently amassing a collection of enums for the SMCCC
|
||||
conduit, and are having to dig into the PSCI driver's internals in order
|
||||
to figure out what to do.
|
||||
|
||||
Let's clean this up, with common SMCCC_CONDUIT_* definitions, and an
|
||||
arm_smccc_1_1_get_conduit() helper that abstracts the PSCI driver's
|
||||
internal state.
|
||||
|
||||
We can kill off the PSCI_CONDUIT_* definitions once we've migrated users
|
||||
over to the new interface.
|
||||
|
||||
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
|
||||
Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
Acked-by: Will Deacon <will.deacon@arm.com>
|
||||
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
|
||||
---
|
||||
drivers/firmware/psci/psci.c | 15 +++++++++++++++
|
||||
include/linux/arm-smccc.h | 16 ++++++++++++++++
|
||||
2 files changed, 31 insertions(+)
|
||||
|
||||
diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
|
||||
index 84f4ff351c62..eb797081d159 100644
|
||||
--- a/drivers/firmware/psci/psci.c
|
||||
+++ b/drivers/firmware/psci/psci.c
|
||||
@@ -57,6 +57,21 @@ struct psci_operations psci_ops = {
|
||||
.smccc_version = SMCCC_VERSION_1_0,
|
||||
};
|
||||
|
||||
+enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void)
|
||||
+{
|
||||
+ if (psci_ops.smccc_version < SMCCC_VERSION_1_1)
|
||||
+ return SMCCC_CONDUIT_NONE;
|
||||
+
|
||||
+ switch (psci_ops.conduit) {
|
||||
+ case PSCI_CONDUIT_SMC:
|
||||
+ return SMCCC_CONDUIT_SMC;
|
||||
+ case PSCI_CONDUIT_HVC:
|
||||
+ return SMCCC_CONDUIT_HVC;
|
||||
+ default:
|
||||
+ return SMCCC_CONDUIT_NONE;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
typedef unsigned long (psci_fn)(unsigned long, unsigned long,
|
||||
unsigned long, unsigned long);
|
||||
static psci_fn *invoke_psci_fn;
|
||||
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
|
||||
index 131edde5d37e..e6d4cb4f61f1 100644
|
||||
--- a/include/linux/arm-smccc.h
|
||||
+++ b/include/linux/arm-smccc.h
|
||||
@@ -80,6 +80,22 @@
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/types.h>
|
||||
+
|
||||
+enum arm_smccc_conduit {
|
||||
+ SMCCC_CONDUIT_NONE,
|
||||
+ SMCCC_CONDUIT_SMC,
|
||||
+ SMCCC_CONDUIT_HVC,
|
||||
+};
|
||||
+
|
||||
+/**
|
||||
+ * arm_smccc_1_1_get_conduit()
|
||||
+ *
|
||||
+ * Returns the conduit to be used for SMCCCv1.1 or later.
|
||||
+ *
|
||||
+ * When SMCCCv1.1 is not present, returns SMCCC_CONDUIT_NONE.
|
||||
+ */
|
||||
+enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void);
|
||||
+
|
||||
/**
|
||||
* struct arm_smccc_res - Result from SMC/HVC call
|
||||
* @a0-a3 result values from registers 0 to 3
|
||||
--
|
||||
2.17.1
|
||||
|
Loading…
Reference in New Issue
Block a user