1 /*- 2 * Copyright (c) 2016 Microsoft Corp. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/systm.h> 33 #include <sys/timetc.h> 34 35 #include <machine/cpufunc.h> 36 #include <machine/cputypes.h> 37 #include <machine/md_var.h> 38 39 #include <dev/hyperv/include/hyperv.h> 40 #include <dev/hyperv/include/hyperv_busdma.h> 41 #include <dev/hyperv/vmbus/hyperv_machdep.h> 42 #include <dev/hyperv/vmbus/hyperv_reg.h> 43 #include <dev/hyperv/vmbus/hyperv_var.h> 44 45 struct hyperv_reftsc_ctx { 46 struct hyperv_reftsc *tsc_ref; 47 struct hyperv_dma tsc_ref_dma; 48 }; 49 50 static struct timecounter hyperv_tsc_timecounter = { 51 .tc_get_timecount = NULL, /* based on CPU vendor. */ 52 .tc_poll_pps = NULL, 53 .tc_counter_mask = 0xffffffff, 54 .tc_frequency = HYPERV_TIMER_FREQ, 55 .tc_name = "Hyper-V-TSC", 56 .tc_quality = 3000, 57 .tc_flags = 0, 58 .tc_priv = NULL 59 }; 60 61 static struct hyperv_reftsc_ctx hyperv_ref_tsc; 62 63 uint64_t 64 hypercall_md(volatile void *hc_addr, uint64_t in_val, 65 uint64_t in_paddr, uint64_t out_paddr) 66 { 67 uint64_t status; 68 69 __asm__ __volatile__ ("mov %0, %%r8" : : "r" (out_paddr): "r8"); 70 __asm__ __volatile__ ("call *%3" : "=a" (status) : 71 "c" (in_val), "d" (in_paddr), "m" (hc_addr)); 72 return (status); 73 } 74 75 #define HYPERV_TSC_TIMECOUNT(fence) \ 76 static u_int \ 77 hyperv_tsc_timecount_##fence(struct timecounter *tc) \ 78 { \ 79 struct hyperv_reftsc *tsc_ref = hyperv_ref_tsc.tsc_ref; \ 80 uint32_t seq; \ 81 \ 82 while ((seq = atomic_load_acq_int(&tsc_ref->tsc_seq)) != 0) { \ 83 uint64_t disc, ret, tsc; \ 84 uint64_t scale = tsc_ref->tsc_scale; \ 85 int64_t ofs = tsc_ref->tsc_ofs; \ 86 \ 87 fence(); \ 88 tsc = rdtsc(); \ 89 \ 90 /* ret = ((tsc * scale) >> 64) + ofs */ \ 91 __asm__ __volatile__ ("mulq %3" : \ 92 "=d" (ret), "=a" (disc) : \ 93 "a" (tsc), "r" (scale)); \ 94 ret += ofs; \ 95 \ 96 atomic_thread_fence_acq(); \ 97 if (tsc_ref->tsc_seq == seq) \ 98 return (ret); \ 99 \ 100 /* Sequence changed; re-sync. */ \ 101 } \ 102 /* Fallback to the generic timecounter, i.e. rdmsr. */ \ 103 return (rdmsr(MSR_HV_TIME_REF_COUNT)); \ 104 } \ 105 struct __hack 106 107 HYPERV_TSC_TIMECOUNT(lfence); 108 HYPERV_TSC_TIMECOUNT(mfence); 109 110 static void 111 hyperv_tsc_tcinit(void *dummy __unused) 112 { 113 uint64_t val, orig; 114 115 if ((hyperv_features & 116 (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC)) != 117 (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC) || 118 (cpu_feature & CPUID_SSE2) == 0) /* SSE2 for mfence/lfence */ 119 return; 120 121 switch (cpu_vendor_id) { 122 case CPU_VENDOR_AMD: 123 hyperv_tsc_timecounter.tc_get_timecount = 124 hyperv_tsc_timecount_mfence; 125 break; 126 127 case CPU_VENDOR_INTEL: 128 hyperv_tsc_timecounter.tc_get_timecount = 129 hyperv_tsc_timecount_lfence; 130 break; 131 132 default: 133 /* Unsupport CPU vendors. */ 134 return; 135 } 136 137 hyperv_ref_tsc.tsc_ref = hyperv_dmamem_alloc(NULL, PAGE_SIZE, 0, 138 sizeof(struct hyperv_reftsc), &hyperv_ref_tsc.tsc_ref_dma, 139 BUS_DMA_WAITOK | BUS_DMA_ZERO); 140 if (hyperv_ref_tsc.tsc_ref == NULL) { 141 printf("hyperv: reftsc page allocation failed\n"); 142 return; 143 } 144 145 orig = rdmsr(MSR_HV_REFERENCE_TSC); 146 val = MSR_HV_REFTSC_ENABLE | (orig & MSR_HV_REFTSC_RSVD_MASK) | 147 ((hyperv_ref_tsc.tsc_ref_dma.hv_paddr >> PAGE_SHIFT) << 148 MSR_HV_REFTSC_PGSHIFT); 149 wrmsr(MSR_HV_REFERENCE_TSC, val); 150 151 /* Register "enlightened" timecounter. */ 152 tc_init(&hyperv_tsc_timecounter); 153 } 154 SYSINIT(hyperv_tsc_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, hyperv_tsc_tcinit, 155 NULL); 156