From 1284ba2380b32bf62789b738dbac4d99da463ea1 Mon Sep 17 00:00:00 2001
From: Ian Campbell <ian.campbell@citrix.com>
Date: Tue, 5 Aug 2014 15:23:23 +0100
Subject: [PATCH] Wallclock time support. Needs much refactoring etc
---
arch/arm/include/asm/xen/hypercall.h | 9 +++
arch/arm/xen/enlighten.c | 121 ++++++++++++++++++++++++++++++++++
arch/arm/xen/hypercall.S | 4 +-
3 files changed, 133 insertions(+), 1 deletion(-)
@@ -35,6 +35,7 @@
#include <xen/interface/xen.h>
#include <xen/interface/sched.h>
+#include <xen/interface/platform.h>
long privcmd_call(unsigned call, unsigned long a1,
unsigned long a2, unsigned long a3,
@@ -49,6 +50,14 @@ int HYPERVISOR_memory_op(unsigned int cmd, void *arg);
int HYPERVISOR_physdev_op(int cmd, void *arg);
int HYPERVISOR_vcpu_op(int cmd, int vcpuid, void *extra_args);
int HYPERVISOR_tmem_op(void *arg);
+int HYPERVISOR_dom0_op_raw(struct xen_platform_op *platform_op);
+static inline int
+HYPERVISOR_dom0_op(struct xen_platform_op *platform_op)
+{
+ platform_op->interface_version = XENPF_INTERFACE_VERSION;
+ return HYPERVISOR_dom0_op_raw(platform_op);
+}
+
int HYPERVISOR_multicall(struct multicall_entry *calls, uint32_t nr);
static inline int
@@ -11,6 +11,7 @@
#include <xen/xenbus.h>
#include <xen/page.h>
#include <xen/interface/sched.h>
+#include <xen/interface/platform.h>
#include <xen/xen-ops.h>
#include <asm/xen/hypervisor.h>
#include <asm/xen/hypercall.h>
@@ -24,6 +25,7 @@
#include <linux/cpuidle.h>
#include <linux/cpufreq.h>
#include <linux/cpu.h>
+#include <linux/pvclock_gtod.h>
#include <linux/mm.h>
@@ -219,6 +221,122 @@ static irqreturn_t xen_arm_callback(int irq, void *arg)
return IRQ_HANDLED;
}
+unsigned long xen_clocksource_read(void)
+{
+ unsigned long val = 0;
+ /* XXX arch timer? Error handling etc etc */
+ if (read_current_timer(&val) < 0)
+ printk("read_current_timer failed?\n");
+ return val;
+}
+
+void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock,
+ struct pvclock_vcpu_time_info *vcpu_time,
+ struct timespec *ts)
+{
+ u32 version;
+ u64 delta;
+ struct timespec now;
+
+ /* get wallclock at system boot */
+ do {
+ version = wall_clock->version;
+ rmb(); /* fetch version before time */
+ now.tv_sec = wall_clock->sec;
+ now.tv_nsec = wall_clock->nsec;
+ rmb(); /* fetch time before checking version */
+ } while ((wall_clock->version & 1) || (version != wall_clock->version));
+
+ //delta = pvclock_clocksource_read(vcpu_time); /* time since system boot */
+ delta = xen_clocksource_read();
+ delta += now.tv_sec * (u64)NSEC_PER_SEC + now.tv_nsec;
+
+ now.tv_nsec = do_div(delta, NSEC_PER_SEC);
+ now.tv_sec = delta;
+
+ set_normalized_timespec(ts, now.tv_sec, now.tv_nsec);
+}
+
+static void xen_read_wallclock(struct timespec *ts)
+{
+ struct shared_info *s = HYPERVISOR_shared_info;
+ struct pvclock_wall_clock *wall_clock = &(s->wc);
+ struct pvclock_vcpu_time_info *vcpu_time;
+
+ vcpu_time = &get_cpu_var(xen_vcpu)->time;
+ pvclock_read_wallclock(wall_clock, vcpu_time, ts);
+ put_cpu_var(xen_vcpu);
+}
+
+/* Parts could be common with x86? */
+static int xen_pvclock_gtod_notify(struct notifier_block *nb,
+ unsigned long was_set, void *priv)
+{
+ /* Protected by the calling core code serialization */
+ static struct timespec next_sync;
+
+ struct xen_platform_op op;
+ struct timespec now;
+
+ now = __current_kernel_time();
+
+ /*
+ * We only take the expensive HV call when the clock was set
+ * or when the 11 minutes RTC synchronization time elapsed.
+ */
+ if (!was_set && timespec_compare(&now, &next_sync) < 0)
+ return NOTIFY_OK;
+
+ op.cmd = XENPF_settime;
+ op.u.settime.secs = now.tv_sec;
+ op.u.settime.nsecs = now.tv_nsec;
+ op.u.settime.system_time = xen_clocksource_read();
+ printk("GTOD: Setting to %ld.%ld at %lld\n",
+ (long)op.u.settime.secs,
+ (long)op.u.settime.nsecs,
+ (long long)op.u.settime.system_time);
+ (void)HYPERVISOR_dom0_op(&op);
+
+ /*
+ * Move the next drift compensation time 11 minutes
+ * ahead. That's emulating the sync_cmos_clock() update for
+ * the hardware RTC.
+ */
+ next_sync = now;
+ next_sync.tv_sec += 11 * 60;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block xen_pvclock_gtod_notifier = {
+ .notifier_call = xen_pvclock_gtod_notify,
+};
+
+static void __init xen_time_init(void)
+{
+ //int cpu = smp_processor_id();
+ struct timespec tp;
+
+ /* Set initial system time with full resolution */
+ xen_read_wallclock(&tp);
+ do_settimeofday(&tp);
+
+ printk("INITIAL TIME %ld.%ld\n",
+ (long)tp.tv_sec,
+ (long)tp.tv_nsec);
+ {
+ struct shared_info *s = HYPERVISOR_shared_info;
+ struct pvclock_wall_clock *wall_clock = &(s->wc);
+ struct pvclock_vcpu_time_info *vcpu_time;
+ printk("SHARED INFO TIME %ld.%ld\n",
+ (long)wall_clock->sec,
+ (long)wall_clock->nsec);
+ }
+
+ if (xen_initial_domain())
+ pvclock_gtod_register_notifier(&xen_pvclock_gtod_notifier);
+}
+
/*
* see Documentation/devicetree/bindings/arm/xen.txt for the
* documentation of the Xen Device Tree format.
@@ -323,6 +441,8 @@ static int __init xen_guest_init(void)
register_cpu_notifier(&xen_cpu_notifier);
+ xen_time_init();
+
return 0;
}
early_initcall(xen_guest_init);
@@ -359,4 +479,5 @@ EXPORT_SYMBOL_GPL(HYPERVISOR_physdev_op);
EXPORT_SYMBOL_GPL(HYPERVISOR_vcpu_op);
EXPORT_SYMBOL_GPL(HYPERVISOR_tmem_op);
EXPORT_SYMBOL_GPL(HYPERVISOR_multicall);
+EXPORT_SYMBOL_GPL(HYPERVISOR_dom0_op_raw);
EXPORT_SYMBOL_GPL(privcmd_call);
@@ -78,7 +78,6 @@ ENTRY(HYPERVISOR_##hypercall) \
ENDPROC(HYPERVISOR_##hypercall)
.text
-
HYPERCALL2(xen_version);
HYPERCALL3(console_io);
HYPERCALL3(grant_table_op);
@@ -91,6 +90,9 @@ HYPERCALL3(vcpu_op);
HYPERCALL1(tmem_op);
HYPERCALL2(multicall);
+#define __HYPERVISOR_dom0_op_raw __HYPERVISOR_dom0_op /* Hack to allow setting interface version from C wrapper */
+HYPERCALL1(dom0_op_raw);/*XXX legacy name for what is now platform_op, used by x86 too*/
+
ENTRY(privcmd_call)
stmdb sp!, {r4}
mov r12, r0
--
1.7.10.4