From: Jason Wang <jasowang@redhat.com>
To: mtosatti@redhat.com, avi@redhat.com, kvm@vger.kernel.org
Subject: [PATCH 2/3] test: Add kvmclock driver
Date: Sun, 26 Jul 2009 11:22:45 +0800 [thread overview]
Message-ID: <20090726032245.8068.10750.stgit@FreeLancer> (raw)
In-Reply-To: <20090726032237.8068.32553.stgit@FreeLancer>
This patch add a kvmclock driver for unittest. A special bit:
PV_CLOCK_CYCLE_RAW_TEST_BIT is used to notify the driver to return
just the raw cycle supplied by the hypervisor.
Signed-off-by: Jason Wang <jasowang@redhat.com>
---
kvm/test/x86/kvmclock.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++
kvm/test/x86/kvmclock.h | 51 ++++++++++++
2 files changed, 257 insertions(+), 0 deletions(-)
create mode 100644 kvm/test/x86/kvmclock.c
create mode 100644 kvm/test/x86/kvmclock.h
diff --git a/kvm/test/x86/kvmclock.c b/kvm/test/x86/kvmclock.c
new file mode 100644
index 0000000..1520ea4
--- /dev/null
+++ b/kvm/test/x86/kvmclock.c
@@ -0,0 +1,206 @@
+#include "libcflat.h"
+#include "smp.h"
+#include "kvmclock.h"
+
+#define barrier() asm volatile("":::"memory")
+#define rmb() asm volatile("lfence":::"memory")
+
+struct pvclock_vcpu_time_info hv_clock[MAX_CPU];
+struct pvclock_wall_clock wall_clock;
+static unsigned char valid_flags = 0;
+static u64 __attribute__((aligned(8))) last_value = 0;
+
+#ifdef __i386__
+static inline u64 atomic64_read(const u64 *v)
+{
+ u64 res;
+
+ asm volatile("mov %%ebx, %%eax;"
+ "mov %%ecx, %%edx;"
+ "lock cmpxchg8b %1;"
+ : "=&A" (res)
+ : "m" (*v)
+ );
+
+ return res;
+}
+
+static u64 atomic64_cmpxchg(u64 *v, u64 old, u64 new)
+{
+ u32 low = new;
+ u32 high = new >> 32;
+
+ asm volatile("lock cmpxchg8b %1\n"
+ : "+A" (old), "+m" (*v)
+ : "b" (low), "c" (high)
+ );
+
+ return old;
+}
+#elif defined(__x86_64__)
+static inline u64 atomic64_read(const u64 *v)
+{
+ return (*(volatile long *)v);
+}
+
+static u64 atomic64_cmpxchg(u64 *v, u64 old, u64 new)
+{
+ u64 ret;
+ u64 _old = old;
+ u64 _new = new;
+
+ asm volatile("lock cmpxchgq %1,%2"
+ : "=a" (ret)
+ : "r" (_new), "m" (*(volatile long *)v), "0" (_old)
+ : "memory"
+ );
+ return ret;
+}
+#endif
+
+static void wrmsr(unsigned index, u64 value)
+{
+ unsigned a = value, d = value >> 32;
+
+ asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(index));
+}
+
+static u64 native_read_tsc(void)
+{
+ unsigned a, d;
+
+ asm volatile("rdtsc" : "=a"(a), "=d"(d));
+ return a | (u64)d << 32;
+}
+
+static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift)
+{
+ u64 product;
+#ifdef __i386__
+ u32 tmp1, tmp2;
+#endif
+
+ if (shift < 0)
+ delta >>= -shift;
+ else
+ delta <<= shift;
+
+#ifdef __i386__
+ __asm__ (
+ "mul %5 ; "
+ "mov %4,%%eax ; "
+ "mov %%edx,%4 ; "
+ "mul %5 ; "
+ "xor %5,%5 ; "
+ "add %4,%%eax ; "
+ "adc %5,%%edx ; "
+ : "=A" (product), "=r" (tmp1), "=r" (tmp2)
+ : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) );
+#elif defined(__x86_64__)
+ __asm__ (
+ "mul %%rdx ; shrd $32,%%rdx,%%rax"
+ : "=a" (product) : "0" (delta), "d" ((u64)mul_frac) );
+#else
+#error implement me!
+#endif
+
+ return product;
+}
+
+static unsigned pvclock_get_time_values(struct pvclock_shadow_time *dst,
+ struct pvclock_vcpu_time_info *src)
+{
+ do {
+ dst->version = src->version;
+ rmb(); /* fetch version before data */
+ dst->tsc_timestamp = src->tsc_timestamp;
+ dst->system_timestamp = src->system_time;
+ dst->tsc_to_nsec_mul = src->tsc_to_system_mul;
+ dst->tsc_shift = src->tsc_shift;
+ dst->flags = src->flags;
+ rmb(); /* test version after fetching data */
+ } while ((src->version & 1) || (dst->version != src->version));
+
+ return dst->version;
+}
+
+static u64 pvclock_get_nsec_offset(struct pvclock_shadow_time *shadow)
+{
+ u64 delta = native_read_tsc() - shadow->tsc_timestamp;
+ return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift);
+}
+
+static cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
+{
+ struct pvclock_shadow_time shadow;
+ unsigned version;
+ cycle_t ret, offset;
+ u64 last;
+
+ do {
+ version = pvclock_get_time_values(&shadow, src);
+ barrier();
+ offset = pvclock_get_nsec_offset(&shadow);
+ ret = shadow.system_timestamp + offset;
+ barrier();
+ } while (version != src->version);
+
+ if ((valid_flags & PVCLOCK_CYCLE_RAW_TEST_BIT) ||
+ ((valid_flags & PVCLOCK_TSC_STABLE_BIT) &&
+ (shadow.flags & PVCLOCK_TSC_STABLE_BIT)))
+ return ret;
+
+ last = atomic64_read(&last_value);
+ do {
+ if (ret < last)
+ return last;
+ last = atomic64_cmpxchg(&last_value, last, ret);
+ } while (last != ret);
+
+ return ret;
+}
+
+cycle_t kvm_clock_read()
+{
+ struct pvclock_vcpu_time_info *src;
+ cycle_t ret;
+ int index = smp_id();
+
+ src = &hv_clock[index];
+ ret = pvclock_clocksource_read(src);
+ return ret;
+}
+
+void kvm_clock_init(void *data)
+{
+ int index = smp_id();
+ struct pvclock_vcpu_time_info *hvc = &hv_clock[index];
+
+ printf("kvm-clock: cpu %d, msr 0x:%lx \n", index, hvc);
+ wrmsr(MSR_KVM_SYSTEM_TIME, (unsigned long)hvc | 1);
+}
+
+void kvm_clock_clear(void *data)
+{
+ wrmsr(MSR_KVM_SYSTEM_TIME, 0LL);
+}
+
+void kvm_get_wallclock(struct timespec *ts)
+{
+ u32 version;
+ wrmsr(MSR_KVM_WALL_CLOCK, (u64)&wall_clock);
+
+ do {
+ version = wall_clock.version;
+ rmb(); /* fetch version before time */
+ ts->sec = wall_clock.sec;
+ ts->nsec = wall_clock.nsec;
+ rmb(); /* fetch time before checking version */
+ } while ((wall_clock.version & 1) || (version != wall_clock.version));
+
+}
+
+void pvclock_set_flags(unsigned char flags)
+{
+ valid_flags = flags;
+}
diff --git a/kvm/test/x86/kvmclock.h b/kvm/test/x86/kvmclock.h
new file mode 100644
index 0000000..6cc545e
--- /dev/null
+++ b/kvm/test/x86/kvmclock.h
@@ -0,0 +1,51 @@
+#ifndef KVMCLOCK_H
+#define KVMCLOCK_H
+
+#define MSR_KVM_WALL_CLOCK 0x11
+#define MSR_KVM_SYSTEM_TIME 0x12
+
+#define MAX_CPU 4
+
+#define PVCLOCK_TSC_STABLE_BIT (1 << 0)
+#define PVCLOCK_CYCLE_RAW_TEST_BIT (1 << 1) /* Get raw cycle */
+
+typedef u64 cycle_t;
+
+struct pvclock_vcpu_time_info {
+ u32 version;
+ u32 pad0;
+ u64 tsc_timestamp;
+ u64 system_time;
+ u32 tsc_to_system_mul;
+ signed char tsc_shift;
+ u8 flags;
+ u8 pad[2];
+} __attribute__((__packed__)); /* 32 bytes */
+
+struct pvclock_shadow_time {
+ u64 tsc_timestamp; /* TSC at last update of time vals. */
+ u64 system_timestamp; /* Time, in nanosecs, since boot. */
+ u32 tsc_to_nsec_mul;
+ int tsc_shift;
+ u32 version;
+ u8 flags;
+};
+
+struct pvclock_wall_clock {
+ u32 version;
+ u32 sec;
+ u32 nsec;
+} __attribute__((__packed__));
+
+struct timespec {
+ u32 sec;
+ u32 nsec;
+};
+
+void pvclock_set_flags(unsigned char flags);
+cycle_t kvm_clock_read();
+void kvm_get_wallclock(struct timespec *ts);
+void kvm_clock_init(void *data);
+void kvm_clock_clear(void *data);
+
+#endif
next prev parent reply other threads:[~2010-07-26 3:22 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-07-26 3:22 [PATCH 1/3] test: Drop print.S Jason Wang
2009-07-26 3:22 ` Jason Wang [this message]
2009-07-26 3:22 ` [PATCH 3/3] test: Add test for kvmclock Jason Wang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20090726032245.8068.10750.stgit@FreeLancer \
--to=jasowang@redhat.com \
--cc=avi@redhat.com \
--cc=kvm@vger.kernel.org \
--cc=mtosatti@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.