1*907ce0f7SPaolo Bonzini #include "libcflat.h" 2*907ce0f7SPaolo Bonzini #include "smp.h" 3*907ce0f7SPaolo Bonzini #include "atomic.h" 4*907ce0f7SPaolo Bonzini #include "processor.h" 5*907ce0f7SPaolo Bonzini #include "hyperv.h" 6*907ce0f7SPaolo Bonzini #include "vm.h" 7*907ce0f7SPaolo Bonzini 8*907ce0f7SPaolo Bonzini #define MAX_CPU 4 9*907ce0f7SPaolo Bonzini #define TICKS_PER_SEC (1000000000 / 100) 10*907ce0f7SPaolo Bonzini 11*907ce0f7SPaolo Bonzini struct hv_reference_tsc_page *hv_clock; 12*907ce0f7SPaolo Bonzini 13*907ce0f7SPaolo Bonzini /* 14*907ce0f7SPaolo Bonzini * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, 15*907ce0f7SPaolo Bonzini * yielding a 64-bit result. 16*907ce0f7SPaolo Bonzini */ 17*907ce0f7SPaolo Bonzini static inline u64 scale_delta(u64 delta, u64 mul_frac) 18*907ce0f7SPaolo Bonzini { 19*907ce0f7SPaolo Bonzini u64 product, unused; 20*907ce0f7SPaolo Bonzini 21*907ce0f7SPaolo Bonzini __asm__ ( 22*907ce0f7SPaolo Bonzini "mul %3" 23*907ce0f7SPaolo Bonzini : "=d" (product), "=a" (unused) : "1" (delta), "rm" ((u64)mul_frac) ); 24*907ce0f7SPaolo Bonzini 25*907ce0f7SPaolo Bonzini return product; 26*907ce0f7SPaolo Bonzini } 27*907ce0f7SPaolo Bonzini 28*907ce0f7SPaolo Bonzini static u64 hvclock_tsc_to_ticks(struct hv_reference_tsc_page *shadow, uint64_t tsc) 29*907ce0f7SPaolo Bonzini { 30*907ce0f7SPaolo Bonzini u64 delta = tsc; 31*907ce0f7SPaolo Bonzini return scale_delta(delta, shadow->tsc_scale) + shadow->tsc_offset; 32*907ce0f7SPaolo Bonzini } 33*907ce0f7SPaolo Bonzini 34*907ce0f7SPaolo Bonzini /* 35*907ce0f7SPaolo Bonzini * Reads a consistent set of time-base values from hypervisor, 36*907ce0f7SPaolo Bonzini * into a shadow data area. 37*907ce0f7SPaolo Bonzini */ 38*907ce0f7SPaolo Bonzini static void hvclock_get_time_values(struct hv_reference_tsc_page *shadow, 39*907ce0f7SPaolo Bonzini struct hv_reference_tsc_page *page) 40*907ce0f7SPaolo Bonzini { 41*907ce0f7SPaolo Bonzini int seq; 42*907ce0f7SPaolo Bonzini do { 43*907ce0f7SPaolo Bonzini seq = page->tsc_sequence; 44*907ce0f7SPaolo Bonzini rmb(); /* fetch version before data */ 45*907ce0f7SPaolo Bonzini *shadow = *page; 46*907ce0f7SPaolo Bonzini rmb(); /* test version after fetching data */ 47*907ce0f7SPaolo Bonzini } while (shadow->tsc_sequence != seq); 48*907ce0f7SPaolo Bonzini } 49*907ce0f7SPaolo Bonzini 50*907ce0f7SPaolo Bonzini uint64_t hv_clock_read(void) 51*907ce0f7SPaolo Bonzini { 52*907ce0f7SPaolo Bonzini struct hv_reference_tsc_page shadow; 53*907ce0f7SPaolo Bonzini 54*907ce0f7SPaolo Bonzini hvclock_get_time_values(&shadow, hv_clock); 55*907ce0f7SPaolo Bonzini return hvclock_tsc_to_ticks(&shadow, rdtsc()); 56*907ce0f7SPaolo Bonzini } 57*907ce0f7SPaolo Bonzini 58*907ce0f7SPaolo Bonzini atomic_t cpus_left; 59*907ce0f7SPaolo Bonzini bool ok[MAX_CPU]; 60*907ce0f7SPaolo Bonzini uint64_t loops[MAX_CPU]; 61*907ce0f7SPaolo Bonzini 62*907ce0f7SPaolo Bonzini #define iabs(x) ((x) < 0 ? -(x) : (x)) 63*907ce0f7SPaolo Bonzini 64*907ce0f7SPaolo Bonzini static void hv_clock_test(void *data) 65*907ce0f7SPaolo Bonzini { 66*907ce0f7SPaolo Bonzini int i = smp_id(); 67*907ce0f7SPaolo Bonzini uint64_t t = rdmsr(HV_X64_MSR_TIME_REF_COUNT); 68*907ce0f7SPaolo Bonzini uint64_t end = t + 3 * TICKS_PER_SEC; 69*907ce0f7SPaolo Bonzini uint64_t msr_sample = t + TICKS_PER_SEC; 70*907ce0f7SPaolo Bonzini int min_delta = 123456, max_delta = -123456; 71*907ce0f7SPaolo Bonzini bool got_drift = false; 72*907ce0f7SPaolo Bonzini bool got_warp = false; 73*907ce0f7SPaolo Bonzini 74*907ce0f7SPaolo Bonzini ok[i] = true; 75*907ce0f7SPaolo Bonzini do { 76*907ce0f7SPaolo Bonzini uint64_t now = hv_clock_read(); 77*907ce0f7SPaolo Bonzini int delta = rdmsr(HV_X64_MSR_TIME_REF_COUNT) - now; 78*907ce0f7SPaolo Bonzini 79*907ce0f7SPaolo Bonzini min_delta = delta < min_delta ? delta : min_delta; 80*907ce0f7SPaolo Bonzini if (t < msr_sample) { 81*907ce0f7SPaolo Bonzini max_delta = delta > max_delta ? delta: max_delta; 82*907ce0f7SPaolo Bonzini } else if (delta < 0 || delta > max_delta * 3 / 2) { 83*907ce0f7SPaolo Bonzini printf("suspecting drift on CPU %d? delta = %d, acceptable [0, %d)\n", smp_id(), 84*907ce0f7SPaolo Bonzini delta, max_delta); 85*907ce0f7SPaolo Bonzini ok[i] = false; 86*907ce0f7SPaolo Bonzini got_drift = true; 87*907ce0f7SPaolo Bonzini max_delta *= 2; 88*907ce0f7SPaolo Bonzini } 89*907ce0f7SPaolo Bonzini 90*907ce0f7SPaolo Bonzini if (now < t && !got_warp) { 91*907ce0f7SPaolo Bonzini printf("warp on CPU %d!\n", smp_id()); 92*907ce0f7SPaolo Bonzini ok[i] = false; 93*907ce0f7SPaolo Bonzini got_warp = true; 94*907ce0f7SPaolo Bonzini break; 95*907ce0f7SPaolo Bonzini } 96*907ce0f7SPaolo Bonzini t = now; 97*907ce0f7SPaolo Bonzini } while(t < end); 98*907ce0f7SPaolo Bonzini 99*907ce0f7SPaolo Bonzini if (!got_drift) 100*907ce0f7SPaolo Bonzini printf("delta on CPU %d was %d...%d\n", smp_id(), min_delta, max_delta); 101*907ce0f7SPaolo Bonzini barrier(); 102*907ce0f7SPaolo Bonzini atomic_dec(&cpus_left); 103*907ce0f7SPaolo Bonzini } 104*907ce0f7SPaolo Bonzini 105*907ce0f7SPaolo Bonzini static void check_test(int ncpus) 106*907ce0f7SPaolo Bonzini { 107*907ce0f7SPaolo Bonzini int i; 108*907ce0f7SPaolo Bonzini bool pass; 109*907ce0f7SPaolo Bonzini 110*907ce0f7SPaolo Bonzini atomic_set(&cpus_left, ncpus); 111*907ce0f7SPaolo Bonzini for (i = ncpus - 1; i >= 0; i--) 112*907ce0f7SPaolo Bonzini on_cpu_async(i, hv_clock_test, NULL); 113*907ce0f7SPaolo Bonzini 114*907ce0f7SPaolo Bonzini /* Wait for the end of other vcpu */ 115*907ce0f7SPaolo Bonzini while(atomic_read(&cpus_left)) 116*907ce0f7SPaolo Bonzini ; 117*907ce0f7SPaolo Bonzini 118*907ce0f7SPaolo Bonzini pass = true; 119*907ce0f7SPaolo Bonzini for (i = ncpus - 1; i >= 0; i--) 120*907ce0f7SPaolo Bonzini pass &= ok[i]; 121*907ce0f7SPaolo Bonzini 122*907ce0f7SPaolo Bonzini report("TSC reference precision test", pass); 123*907ce0f7SPaolo Bonzini } 124*907ce0f7SPaolo Bonzini 125*907ce0f7SPaolo Bonzini static void hv_perf_test(void *data) 126*907ce0f7SPaolo Bonzini { 127*907ce0f7SPaolo Bonzini uint64_t t = hv_clock_read(); 128*907ce0f7SPaolo Bonzini uint64_t end = t + 1000000000 / 100; 129*907ce0f7SPaolo Bonzini uint64_t local_loops = 0; 130*907ce0f7SPaolo Bonzini 131*907ce0f7SPaolo Bonzini do { 132*907ce0f7SPaolo Bonzini t = hv_clock_read(); 133*907ce0f7SPaolo Bonzini local_loops++; 134*907ce0f7SPaolo Bonzini } while(t < end); 135*907ce0f7SPaolo Bonzini 136*907ce0f7SPaolo Bonzini loops[smp_id()] = local_loops; 137*907ce0f7SPaolo Bonzini atomic_dec(&cpus_left); 138*907ce0f7SPaolo Bonzini } 139*907ce0f7SPaolo Bonzini 140*907ce0f7SPaolo Bonzini static void perf_test(int ncpus) 141*907ce0f7SPaolo Bonzini { 142*907ce0f7SPaolo Bonzini int i; 143*907ce0f7SPaolo Bonzini uint64_t total_loops; 144*907ce0f7SPaolo Bonzini 145*907ce0f7SPaolo Bonzini atomic_set(&cpus_left, ncpus); 146*907ce0f7SPaolo Bonzini for (i = ncpus - 1; i >= 0; i--) 147*907ce0f7SPaolo Bonzini on_cpu_async(i, hv_perf_test, NULL); 148*907ce0f7SPaolo Bonzini 149*907ce0f7SPaolo Bonzini /* Wait for the end of other vcpu */ 150*907ce0f7SPaolo Bonzini while(atomic_read(&cpus_left)) 151*907ce0f7SPaolo Bonzini ; 152*907ce0f7SPaolo Bonzini 153*907ce0f7SPaolo Bonzini total_loops = 0; 154*907ce0f7SPaolo Bonzini for (i = ncpus - 1; i >= 0; i--) 155*907ce0f7SPaolo Bonzini total_loops += loops[i]; 156*907ce0f7SPaolo Bonzini printf("iterations/sec: %" PRId64"\n", total_loops / ncpus); 157*907ce0f7SPaolo Bonzini } 158*907ce0f7SPaolo Bonzini 159*907ce0f7SPaolo Bonzini int main(int ac, char **av) 160*907ce0f7SPaolo Bonzini { 161*907ce0f7SPaolo Bonzini int nerr = 0; 162*907ce0f7SPaolo Bonzini int ncpus; 163*907ce0f7SPaolo Bonzini struct hv_reference_tsc_page shadow; 164*907ce0f7SPaolo Bonzini uint64_t tsc1, t1, tsc2, t2; 165*907ce0f7SPaolo Bonzini uint64_t ref1, ref2; 166*907ce0f7SPaolo Bonzini 167*907ce0f7SPaolo Bonzini setup_vm(); 168*907ce0f7SPaolo Bonzini smp_init(); 169*907ce0f7SPaolo Bonzini 170*907ce0f7SPaolo Bonzini hv_clock = alloc_page(); 171*907ce0f7SPaolo Bonzini wrmsr(HV_X64_MSR_REFERENCE_TSC, (u64)(uintptr_t)hv_clock | 1); 172*907ce0f7SPaolo Bonzini report("MSR value after enabling", 173*907ce0f7SPaolo Bonzini rdmsr(HV_X64_MSR_REFERENCE_TSC) == ((u64)(uintptr_t)hv_clock | 1)); 174*907ce0f7SPaolo Bonzini 175*907ce0f7SPaolo Bonzini hvclock_get_time_values(&shadow, hv_clock); 176*907ce0f7SPaolo Bonzini if (shadow.tsc_sequence == 0 || shadow.tsc_sequence == 0xFFFFFFFF) { 177*907ce0f7SPaolo Bonzini printf("Reference TSC page not available\n"); 178*907ce0f7SPaolo Bonzini exit(1); 179*907ce0f7SPaolo Bonzini } 180*907ce0f7SPaolo Bonzini 181*907ce0f7SPaolo Bonzini printf("scale: %" PRIx64" offset: %" PRId64"\n", shadow.tsc_scale, shadow.tsc_offset); 182*907ce0f7SPaolo Bonzini ref1 = rdmsr(HV_X64_MSR_TIME_REF_COUNT); 183*907ce0f7SPaolo Bonzini tsc1 = rdtsc(); 184*907ce0f7SPaolo Bonzini t1 = hvclock_tsc_to_ticks(&shadow, tsc1); 185*907ce0f7SPaolo Bonzini printf("refcnt %" PRId64", TSC %" PRIx64", TSC reference %" PRId64"\n", 186*907ce0f7SPaolo Bonzini ref1, tsc1, t1); 187*907ce0f7SPaolo Bonzini 188*907ce0f7SPaolo Bonzini do 189*907ce0f7SPaolo Bonzini ref2 = rdmsr(HV_X64_MSR_TIME_REF_COUNT); 190*907ce0f7SPaolo Bonzini while (ref2 < ref1 + 2 * TICKS_PER_SEC); 191*907ce0f7SPaolo Bonzini 192*907ce0f7SPaolo Bonzini tsc2 = rdtsc(); 193*907ce0f7SPaolo Bonzini t2 = hvclock_tsc_to_ticks(&shadow, tsc2); 194*907ce0f7SPaolo Bonzini printf("refcnt %" PRId64" (delta %" PRId64"), TSC %" PRIx64", " 195*907ce0f7SPaolo Bonzini "TSC reference %" PRId64" (delta %" PRId64")\n", 196*907ce0f7SPaolo Bonzini ref2, ref2 - ref1, tsc2, t2, t2 - t1); 197*907ce0f7SPaolo Bonzini 198*907ce0f7SPaolo Bonzini ncpus = cpu_count(); 199*907ce0f7SPaolo Bonzini if (ncpus > MAX_CPU) 200*907ce0f7SPaolo Bonzini ncpus = MAX_CPU; 201*907ce0f7SPaolo Bonzini 202*907ce0f7SPaolo Bonzini check_test(ncpus); 203*907ce0f7SPaolo Bonzini perf_test(ncpus); 204*907ce0f7SPaolo Bonzini 205*907ce0f7SPaolo Bonzini wrmsr(HV_X64_MSR_REFERENCE_TSC, 0LL); 206*907ce0f7SPaolo Bonzini report("MSR value after disabling", rdmsr(HV_X64_MSR_REFERENCE_TSC) == 0); 207*907ce0f7SPaolo Bonzini 208*907ce0f7SPaolo Bonzini return nerr > 0 ? 1 : 0; 209*907ce0f7SPaolo Bonzini } 210