1907ce0f7SPaolo Bonzini #include "libcflat.h"
2907ce0f7SPaolo Bonzini #include "smp.h"
3907ce0f7SPaolo Bonzini #include "atomic.h"
4907ce0f7SPaolo Bonzini #include "processor.h"
5907ce0f7SPaolo Bonzini #include "hyperv.h"
6907ce0f7SPaolo Bonzini #include "vm.h"
75aca024eSPaolo Bonzini #include "alloc_page.h"
8907ce0f7SPaolo Bonzini
9907ce0f7SPaolo Bonzini #define MAX_CPU 4
10907ce0f7SPaolo Bonzini #define TICKS_PER_SEC (1000000000 / 100)
11907ce0f7SPaolo Bonzini
12907ce0f7SPaolo Bonzini struct hv_reference_tsc_page *hv_clock;
13907ce0f7SPaolo Bonzini
14907ce0f7SPaolo Bonzini /*
15907ce0f7SPaolo Bonzini * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction,
16907ce0f7SPaolo Bonzini * yielding a 64-bit result.
17907ce0f7SPaolo Bonzini */
scale_delta(u64 delta,u64 mul_frac)18907ce0f7SPaolo Bonzini static inline u64 scale_delta(u64 delta, u64 mul_frac)
19907ce0f7SPaolo Bonzini {
20907ce0f7SPaolo Bonzini u64 product, unused;
21907ce0f7SPaolo Bonzini
22907ce0f7SPaolo Bonzini __asm__ (
2345276b58SThomas Petazzoni "mulq %3"
24907ce0f7SPaolo Bonzini : "=d" (product), "=a" (unused) : "1" (delta), "rm" ((u64)mul_frac) );
25907ce0f7SPaolo Bonzini
26907ce0f7SPaolo Bonzini return product;
27907ce0f7SPaolo Bonzini }
28907ce0f7SPaolo Bonzini
hvclock_tsc_to_ticks(struct hv_reference_tsc_page * shadow,uint64_t tsc)29907ce0f7SPaolo Bonzini static u64 hvclock_tsc_to_ticks(struct hv_reference_tsc_page *shadow, uint64_t tsc)
30907ce0f7SPaolo Bonzini {
31907ce0f7SPaolo Bonzini u64 delta = tsc;
32907ce0f7SPaolo Bonzini return scale_delta(delta, shadow->tsc_scale) + shadow->tsc_offset;
33907ce0f7SPaolo Bonzini }
34907ce0f7SPaolo Bonzini
35907ce0f7SPaolo Bonzini /*
36907ce0f7SPaolo Bonzini * Reads a consistent set of time-base values from hypervisor,
37907ce0f7SPaolo Bonzini * into a shadow data area.
38907ce0f7SPaolo Bonzini */
hvclock_get_time_values(struct hv_reference_tsc_page * shadow,struct hv_reference_tsc_page * page)39907ce0f7SPaolo Bonzini static void hvclock_get_time_values(struct hv_reference_tsc_page *shadow,
40907ce0f7SPaolo Bonzini struct hv_reference_tsc_page *page)
41907ce0f7SPaolo Bonzini {
42907ce0f7SPaolo Bonzini int seq;
43907ce0f7SPaolo Bonzini do {
44907ce0f7SPaolo Bonzini seq = page->tsc_sequence;
45907ce0f7SPaolo Bonzini rmb(); /* fetch version before data */
46907ce0f7SPaolo Bonzini *shadow = *page;
47907ce0f7SPaolo Bonzini rmb(); /* test version after fetching data */
48907ce0f7SPaolo Bonzini } while (shadow->tsc_sequence != seq);
49907ce0f7SPaolo Bonzini }
50907ce0f7SPaolo Bonzini
hv_clock_read(void)51db4898e8SThomas Huth static uint64_t hv_clock_read(void)
52907ce0f7SPaolo Bonzini {
53907ce0f7SPaolo Bonzini struct hv_reference_tsc_page shadow;
54907ce0f7SPaolo Bonzini
55907ce0f7SPaolo Bonzini hvclock_get_time_values(&shadow, hv_clock);
56907ce0f7SPaolo Bonzini return hvclock_tsc_to_ticks(&shadow, rdtsc());
57907ce0f7SPaolo Bonzini }
58907ce0f7SPaolo Bonzini
59907ce0f7SPaolo Bonzini bool ok[MAX_CPU];
60907ce0f7SPaolo Bonzini uint64_t loops[MAX_CPU];
61907ce0f7SPaolo Bonzini
62907ce0f7SPaolo Bonzini #define iabs(x) ((x) < 0 ? -(x) : (x))
63907ce0f7SPaolo Bonzini
hv_clock_test(void * data)64907ce0f7SPaolo Bonzini static void hv_clock_test(void *data)
65907ce0f7SPaolo Bonzini {
66b78f9e64SMetin Kaya int i = (long)data;
67*bf2c53fdSVitaly Kuznetsov uint64_t t_msr_prev = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
68*bf2c53fdSVitaly Kuznetsov uint64_t t_page_prev = hv_clock_read();
69*bf2c53fdSVitaly Kuznetsov uint64_t end = t_page_prev + TICKS_PER_SEC;
70907ce0f7SPaolo Bonzini bool got_drift = false;
71*bf2c53fdSVitaly Kuznetsov bool got_warp_msr = false;
72*bf2c53fdSVitaly Kuznetsov bool got_warp_page = false;
73907ce0f7SPaolo Bonzini
74907ce0f7SPaolo Bonzini ok[i] = true;
75907ce0f7SPaolo Bonzini do {
76*bf2c53fdSVitaly Kuznetsov uint64_t t_page_1, t_page_2, t_msr;
77907ce0f7SPaolo Bonzini
78*bf2c53fdSVitaly Kuznetsov t_page_1 = hv_clock_read();
79*bf2c53fdSVitaly Kuznetsov barrier();
80*bf2c53fdSVitaly Kuznetsov t_msr = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
81*bf2c53fdSVitaly Kuznetsov barrier();
82*bf2c53fdSVitaly Kuznetsov t_page_2 = hv_clock_read();
83*bf2c53fdSVitaly Kuznetsov
84*bf2c53fdSVitaly Kuznetsov if (!got_drift && (t_msr < t_page_1 || t_msr > t_page_2)) {
85*bf2c53fdSVitaly Kuznetsov printf("drift on CPU %d, MSR value = %ld, acceptable [%ld, %ld]\n", i,
86*bf2c53fdSVitaly Kuznetsov t_msr, t_page_1, t_page_2);
87907ce0f7SPaolo Bonzini ok[i] = false;
88907ce0f7SPaolo Bonzini got_drift = true;
89907ce0f7SPaolo Bonzini }
90907ce0f7SPaolo Bonzini
91*bf2c53fdSVitaly Kuznetsov if (!got_warp_msr && t_msr < t_msr_prev) {
92*bf2c53fdSVitaly Kuznetsov printf("warp on CPU %d, MSR value = %ld prev MSR value = %ld!\n", i,
93*bf2c53fdSVitaly Kuznetsov t_msr, t_msr_prev);
94907ce0f7SPaolo Bonzini ok[i] = false;
95*bf2c53fdSVitaly Kuznetsov got_warp_msr = true;
96907ce0f7SPaolo Bonzini break;
97907ce0f7SPaolo Bonzini }
98907ce0f7SPaolo Bonzini
99*bf2c53fdSVitaly Kuznetsov if (!got_warp_page && t_page_1 < t_page_prev) {
100*bf2c53fdSVitaly Kuznetsov printf("warp on CPU %d, TSC page value = %ld prev TSC page value = %ld!\n", i,
101*bf2c53fdSVitaly Kuznetsov t_page_1, t_page_prev);
102*bf2c53fdSVitaly Kuznetsov ok[i] = false;
103*bf2c53fdSVitaly Kuznetsov got_warp_page = true;
104*bf2c53fdSVitaly Kuznetsov break;
105*bf2c53fdSVitaly Kuznetsov }
106*bf2c53fdSVitaly Kuznetsov
107*bf2c53fdSVitaly Kuznetsov t_page_prev = t_page_1;
108*bf2c53fdSVitaly Kuznetsov t_msr_prev = t_msr;
109*bf2c53fdSVitaly Kuznetsov
110*bf2c53fdSVitaly Kuznetsov } while(t_page_prev < end);
111*bf2c53fdSVitaly Kuznetsov
112907ce0f7SPaolo Bonzini barrier();
113907ce0f7SPaolo Bonzini }
114907ce0f7SPaolo Bonzini
check_test(int ncpus)115907ce0f7SPaolo Bonzini static void check_test(int ncpus)
116907ce0f7SPaolo Bonzini {
117907ce0f7SPaolo Bonzini int i;
118907ce0f7SPaolo Bonzini bool pass;
119907ce0f7SPaolo Bonzini
120b78f9e64SMetin Kaya for (i = ncpus - 1; i >= 0; i--)
121b78f9e64SMetin Kaya on_cpu_async(i, hv_clock_test, (void *)(long)i);
122b78f9e64SMetin Kaya
123b78f9e64SMetin Kaya while (cpus_active() > 1)
124b78f9e64SMetin Kaya pause();
125907ce0f7SPaolo Bonzini
126907ce0f7SPaolo Bonzini pass = true;
127907ce0f7SPaolo Bonzini for (i = ncpus - 1; i >= 0; i--)
128907ce0f7SPaolo Bonzini pass &= ok[i];
129907ce0f7SPaolo Bonzini
130a299895bSThomas Huth report(pass, "TSC reference precision test");
131907ce0f7SPaolo Bonzini }
132907ce0f7SPaolo Bonzini
hv_perf_test(void * data)133907ce0f7SPaolo Bonzini static void hv_perf_test(void *data)
134907ce0f7SPaolo Bonzini {
135b78f9e64SMetin Kaya int i = (long)data;
136907ce0f7SPaolo Bonzini uint64_t t = hv_clock_read();
137907ce0f7SPaolo Bonzini uint64_t end = t + 1000000000 / 100;
138907ce0f7SPaolo Bonzini uint64_t local_loops = 0;
139907ce0f7SPaolo Bonzini
140907ce0f7SPaolo Bonzini do {
141907ce0f7SPaolo Bonzini t = hv_clock_read();
142907ce0f7SPaolo Bonzini local_loops++;
143907ce0f7SPaolo Bonzini } while(t < end);
144907ce0f7SPaolo Bonzini
145b78f9e64SMetin Kaya loops[i] = local_loops;
146907ce0f7SPaolo Bonzini }
147907ce0f7SPaolo Bonzini
perf_test(int ncpus)148907ce0f7SPaolo Bonzini static void perf_test(int ncpus)
149907ce0f7SPaolo Bonzini {
150907ce0f7SPaolo Bonzini int i;
151907ce0f7SPaolo Bonzini uint64_t total_loops;
152907ce0f7SPaolo Bonzini
153b78f9e64SMetin Kaya for (i = ncpus - 1; i >= 0; i--)
154b78f9e64SMetin Kaya on_cpu_async(i, hv_perf_test, (void *)(long)i);
155b78f9e64SMetin Kaya
156b78f9e64SMetin Kaya while (cpus_active() > 1)
157b78f9e64SMetin Kaya pause();
158907ce0f7SPaolo Bonzini
159907ce0f7SPaolo Bonzini total_loops = 0;
160907ce0f7SPaolo Bonzini for (i = ncpus - 1; i >= 0; i--)
161907ce0f7SPaolo Bonzini total_loops += loops[i];
162907ce0f7SPaolo Bonzini printf("iterations/sec: %" PRId64"\n", total_loops / ncpus);
163907ce0f7SPaolo Bonzini }
164907ce0f7SPaolo Bonzini
main(int ac,char ** av)165907ce0f7SPaolo Bonzini int main(int ac, char **av)
166907ce0f7SPaolo Bonzini {
167907ce0f7SPaolo Bonzini int ncpus;
168907ce0f7SPaolo Bonzini struct hv_reference_tsc_page shadow;
169907ce0f7SPaolo Bonzini uint64_t tsc1, t1, tsc2, t2;
170907ce0f7SPaolo Bonzini uint64_t ref1, ref2;
171907ce0f7SPaolo Bonzini
1725067df40SNadav Amit if (!hv_time_ref_counter_supported()) {
1735067df40SNadav Amit report_skip("time reference counter is unsupported");
17487ba477aSVitaly Kuznetsov goto done;
1755067df40SNadav Amit }
1765067df40SNadav Amit
177907ce0f7SPaolo Bonzini setup_vm();
178907ce0f7SPaolo Bonzini
179eb8445a9SAndrew Jones ncpus = cpu_count();
180eb8445a9SAndrew Jones if (ncpus > MAX_CPU)
181eb8445a9SAndrew Jones report_abort("number cpus exceeds %d", MAX_CPU);
182eb8445a9SAndrew Jones
183907ce0f7SPaolo Bonzini hv_clock = alloc_page();
184907ce0f7SPaolo Bonzini wrmsr(HV_X64_MSR_REFERENCE_TSC, (u64)(uintptr_t)hv_clock | 1);
185a299895bSThomas Huth report(rdmsr(HV_X64_MSR_REFERENCE_TSC) == ((u64)(uintptr_t)hv_clock | 1),
186a299895bSThomas Huth "MSR value after enabling");
187907ce0f7SPaolo Bonzini
188907ce0f7SPaolo Bonzini hvclock_get_time_values(&shadow, hv_clock);
18987ba477aSVitaly Kuznetsov if (shadow.tsc_sequence == 0 || shadow.tsc_sequence == 0xFFFFFFFF)
19087ba477aSVitaly Kuznetsov report_abort("Reference TSC page not available\n");
191907ce0f7SPaolo Bonzini
1920d6cb7b2SMetin Kaya printf("sequence: %u. scale: %" PRIx64" offset: %" PRId64"\n",
1930d6cb7b2SMetin Kaya shadow.tsc_sequence, shadow.tsc_scale, shadow.tsc_offset);
194907ce0f7SPaolo Bonzini ref1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
195907ce0f7SPaolo Bonzini tsc1 = rdtsc();
196907ce0f7SPaolo Bonzini t1 = hvclock_tsc_to_ticks(&shadow, tsc1);
197907ce0f7SPaolo Bonzini printf("refcnt %" PRId64", TSC %" PRIx64", TSC reference %" PRId64"\n",
198907ce0f7SPaolo Bonzini ref1, tsc1, t1);
199907ce0f7SPaolo Bonzini
200907ce0f7SPaolo Bonzini do
201907ce0f7SPaolo Bonzini ref2 = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
202907ce0f7SPaolo Bonzini while (ref2 < ref1 + 2 * TICKS_PER_SEC);
203907ce0f7SPaolo Bonzini
204907ce0f7SPaolo Bonzini tsc2 = rdtsc();
205907ce0f7SPaolo Bonzini t2 = hvclock_tsc_to_ticks(&shadow, tsc2);
206907ce0f7SPaolo Bonzini printf("refcnt %" PRId64" (delta %" PRId64"), TSC %" PRIx64", "
207907ce0f7SPaolo Bonzini "TSC reference %" PRId64" (delta %" PRId64")\n",
208907ce0f7SPaolo Bonzini ref2, ref2 - ref1, tsc2, t2, t2 - t1);
209907ce0f7SPaolo Bonzini
210907ce0f7SPaolo Bonzini check_test(ncpus);
211907ce0f7SPaolo Bonzini perf_test(ncpus);
212907ce0f7SPaolo Bonzini
213907ce0f7SPaolo Bonzini wrmsr(HV_X64_MSR_REFERENCE_TSC, 0LL);
214a299895bSThomas Huth report(rdmsr(HV_X64_MSR_REFERENCE_TSC) == 0,
215a299895bSThomas Huth "MSR value after disabling");
216907ce0f7SPaolo Bonzini
21787ba477aSVitaly Kuznetsov done:
21887ba477aSVitaly Kuznetsov return report_summary();
219907ce0f7SPaolo Bonzini }
220