1 /*- 2 * Copyright (c) 2016-2017 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/conf.h> 32 #include <sys/fcntl.h> 33 #include <sys/kernel.h> 34 #include <sys/systm.h> 35 #include <sys/timetc.h> 36 #include <sys/vdso.h> 37 38 #include <machine/cpufunc.h> 39 #include <machine/cputypes.h> 40 #include <machine/md_var.h> 41 #include <machine/specialreg.h> 42 43 #include <vm/vm.h> 44 45 #include <dev/hyperv/include/hyperv.h> 46 #include <dev/hyperv/include/hyperv_busdma.h> 47 #include <dev/hyperv/vmbus/x86/hyperv_machdep.h> 48 #include <dev/hyperv/vmbus/x86/hyperv_reg.h> 49 #include <dev/hyperv/vmbus/hyperv_var.h> 50 #include <dev/hyperv/vmbus/hyperv_common_reg.h> 51 52 struct hyperv_reftsc_ctx { 53 struct hyperv_reftsc *tsc_ref; 54 struct hyperv_dma tsc_ref_dma; 55 }; 56 57 static uint32_t hyperv_tsc_vdso_timehands( 58 struct vdso_timehands *, 59 struct timecounter *); 60 61 static d_open_t hyperv_tsc_open; 62 static d_mmap_t hyperv_tsc_mmap; 63 64 static struct timecounter hyperv_tsc_timecounter = { 65 .tc_get_timecount = NULL, /* based on CPU vendor. */ 66 .tc_counter_mask = 0xffffffff, 67 .tc_frequency = HYPERV_TIMER_FREQ, 68 .tc_name = "Hyper-V-TSC", 69 .tc_quality = 3000, 70 .tc_fill_vdso_timehands = hyperv_tsc_vdso_timehands, 71 }; 72 73 static struct cdevsw hyperv_tsc_cdevsw = { 74 .d_version = D_VERSION, 75 .d_open = hyperv_tsc_open, 76 .d_mmap = hyperv_tsc_mmap, 77 .d_name = HYPERV_REFTSC_DEVNAME 78 }; 79 80 static struct hyperv_reftsc_ctx hyperv_ref_tsc; 81 82 uint64_t 83 hypercall_md(volatile void *hc_addr, uint64_t in_val, 84 uint64_t in_paddr, uint64_t out_paddr) 85 { 86 uint64_t status; 87 88 __asm__ __volatile__ ("mov %0, %%r8" : : "r" (out_paddr): "r8"); 89 __asm__ __volatile__ ("call *%3" : "=a" (status) : 90 "c" (in_val), "d" (in_paddr), "m" (hc_addr)); 91 return (status); 92 } 93 94 static int 95 hyperv_tsc_open(struct cdev *dev __unused, int oflags, int devtype __unused, 96 struct thread *td __unused) 97 { 98 99 if (oflags & FWRITE) 100 return (EPERM); 101 return (0); 102 } 103 104 static int 105 hyperv_tsc_mmap(struct cdev *dev __unused, vm_ooffset_t offset, 106 vm_paddr_t *paddr, int nprot __unused, vm_memattr_t *memattr __unused) 107 { 108 109 KASSERT(hyperv_ref_tsc.tsc_ref != NULL, ("reftsc has not been setup")); 110 111 /* 112 * NOTE: 113 * 'nprot' does not contain information interested to us; 114 * WR-open is blocked by d_open. 115 */ 116 117 if (offset != 0) 118 return (EOPNOTSUPP); 119 120 *paddr = hyperv_ref_tsc.tsc_ref_dma.hv_paddr; 121 return (0); 122 } 123 124 static uint32_t 125 hyperv_tsc_vdso_timehands(struct vdso_timehands *vdso_th, 126 struct timecounter *tc __unused) 127 { 128 129 vdso_th->th_algo = VDSO_TH_ALGO_X86_HVTSC; 130 vdso_th->th_x86_shift = 0; 131 vdso_th->th_x86_hpet_idx = 0; 132 vdso_th->th_x86_pvc_last_systime = 0; 133 vdso_th->th_x86_pvc_stable_mask = 0; 134 bzero(vdso_th->th_res, sizeof(vdso_th->th_res)); 135 return (1); 136 } 137 138 #define HYPERV_TSC_TIMECOUNT(fence) \ 139 static uint64_t \ 140 hyperv_tc64_tsc_##fence(void) \ 141 { \ 142 struct hyperv_reftsc *tsc_ref = hyperv_ref_tsc.tsc_ref; \ 143 uint32_t seq; \ 144 \ 145 while ((seq = atomic_load_acq_int(&tsc_ref->tsc_seq)) != 0) { \ 146 uint64_t disc, ret, tsc; \ 147 uint64_t scale = tsc_ref->tsc_scale; \ 148 int64_t ofs = tsc_ref->tsc_ofs; \ 149 \ 150 fence(); \ 151 tsc = rdtsc(); \ 152 \ 153 /* ret = ((tsc * scale) >> 64) + ofs */ \ 154 __asm__ __volatile__ ("mulq %3" : \ 155 "=d" (ret), "=a" (disc) : \ 156 "a" (tsc), "r" (scale)); \ 157 ret += ofs; \ 158 \ 159 atomic_thread_fence_acq(); \ 160 if (tsc_ref->tsc_seq == seq) \ 161 return (ret); \ 162 \ 163 /* Sequence changed; re-sync. */ \ 164 } \ 165 /* Fallback to the generic timecounter, i.e. rdmsr. */ \ 166 return (rdmsr(MSR_HV_TIME_REF_COUNT)); \ 167 } \ 168 \ 169 static u_int \ 170 hyperv_tsc_timecount_##fence(struct timecounter *tc __unused) \ 171 { \ 172 \ 173 return (hyperv_tc64_tsc_##fence()); \ 174 } \ 175 struct __hack 176 177 HYPERV_TSC_TIMECOUNT(lfence); 178 HYPERV_TSC_TIMECOUNT(mfence); 179 180 static void 181 hyperv_tsc_tcinit(void *dummy __unused) 182 { 183 hyperv_tc64_t tc64 = NULL; 184 uint64_t val, orig; 185 186 if ((hyperv_features & 187 (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC)) != 188 (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC) || 189 (cpu_feature & CPUID_SSE2) == 0) /* SSE2 for mfence/lfence */ 190 return; 191 192 switch (cpu_vendor_id) { 193 case CPU_VENDOR_AMD: 194 case CPU_VENDOR_HYGON: 195 hyperv_tsc_timecounter.tc_get_timecount = 196 hyperv_tsc_timecount_mfence; 197 tc64 = hyperv_tc64_tsc_mfence; 198 break; 199 200 case CPU_VENDOR_INTEL: 201 hyperv_tsc_timecounter.tc_get_timecount = 202 hyperv_tsc_timecount_lfence; 203 tc64 = hyperv_tc64_tsc_lfence; 204 break; 205 206 default: 207 /* Unsupported CPU vendors. */ 208 return; 209 } 210 211 hyperv_ref_tsc.tsc_ref = hyperv_dmamem_alloc(NULL, PAGE_SIZE, 0, 212 sizeof(struct hyperv_reftsc), &hyperv_ref_tsc.tsc_ref_dma, 213 BUS_DMA_WAITOK | BUS_DMA_ZERO); 214 if (hyperv_ref_tsc.tsc_ref == NULL) { 215 printf("hyperv: reftsc page allocation failed\n"); 216 return; 217 } 218 219 orig = rdmsr(MSR_HV_REFERENCE_TSC); 220 val = MSR_HV_REFTSC_ENABLE | (orig & MSR_HV_REFTSC_RSVD_MASK) | 221 ((hyperv_ref_tsc.tsc_ref_dma.hv_paddr >> PAGE_SHIFT) << 222 MSR_HV_REFTSC_PGSHIFT); 223 wrmsr(MSR_HV_REFERENCE_TSC, val); 224 225 /* Register "enlightened" timecounter. */ 226 tc_init(&hyperv_tsc_timecounter); 227 228 /* Install 64 bits timecounter method for other modules to use. */ 229 KASSERT(tc64 != NULL, ("tc64 is not set")); 230 hyperv_tc64 = tc64; 231 232 /* Add device for mmap(2). */ 233 make_dev(&hyperv_tsc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0444, 234 HYPERV_REFTSC_DEVNAME); 235 } 236 SYSINIT(hyperv_tsc_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, hyperv_tsc_tcinit, 237 NULL); 238