1d8bf5168SSepherosa Ziehau /*- 293b4e111SSepherosa Ziehau * Copyright (c) 2016-2017 Microsoft Corp. 3d8bf5168SSepherosa Ziehau * All rights reserved. 4d8bf5168SSepherosa Ziehau * 5d8bf5168SSepherosa Ziehau * Redistribution and use in source and binary forms, with or without 6d8bf5168SSepherosa Ziehau * modification, are permitted provided that the following conditions 7d8bf5168SSepherosa Ziehau * are met: 8d8bf5168SSepherosa Ziehau * 1. Redistributions of source code must retain the above copyright 9d8bf5168SSepherosa Ziehau * notice unmodified, this list of conditions, and the following 10d8bf5168SSepherosa Ziehau * disclaimer. 11d8bf5168SSepherosa Ziehau * 2. Redistributions in binary form must reproduce the above copyright 12d8bf5168SSepherosa Ziehau * notice, this list of conditions and the following disclaimer in the 13d8bf5168SSepherosa Ziehau * documentation and/or other materials provided with the distribution. 14d8bf5168SSepherosa Ziehau * 15d8bf5168SSepherosa Ziehau * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16d8bf5168SSepherosa Ziehau * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17d8bf5168SSepherosa Ziehau * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18d8bf5168SSepherosa Ziehau * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19d8bf5168SSepherosa Ziehau * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20d8bf5168SSepherosa Ziehau * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21d8bf5168SSepherosa Ziehau * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22d8bf5168SSepherosa Ziehau * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23d8bf5168SSepherosa Ziehau * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24d8bf5168SSepherosa Ziehau * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25d8bf5168SSepherosa Ziehau */ 26d8bf5168SSepherosa Ziehau 27d8bf5168SSepherosa Ziehau #include <sys/cdefs.h> 28d8bf5168SSepherosa Ziehau __FBSDID("$FreeBSD$"); 29d8bf5168SSepherosa Ziehau 30d8bf5168SSepherosa Ziehau #include <sys/param.h> 319622c93aSSepherosa Ziehau #include <sys/conf.h> 329622c93aSSepherosa Ziehau #include <sys/fcntl.h> 33de69dfbbSSepherosa Ziehau #include <sys/kernel.h> 34*62f9bcf2SAndrew Turner #include <sys/malloc.h> 35de69dfbbSSepherosa Ziehau #include <sys/systm.h> 36de69dfbbSSepherosa Ziehau #include <sys/timetc.h> 37fff5be0bSSepherosa Ziehau #include <sys/vdso.h> 38de69dfbbSSepherosa Ziehau 39de69dfbbSSepherosa Ziehau #include <machine/cpufunc.h> 40de69dfbbSSepherosa Ziehau #include <machine/cputypes.h> 41de69dfbbSSepherosa Ziehau #include <machine/md_var.h> 429622c93aSSepherosa Ziehau #include <machine/specialreg.h> 439622c93aSSepherosa Ziehau 449622c93aSSepherosa Ziehau #include <vm/vm.h> 45*62f9bcf2SAndrew Turner #include <vm/vm_extern.h> 46*62f9bcf2SAndrew Turner #include <vm/pmap.h> 47de69dfbbSSepherosa Ziehau 48de69dfbbSSepherosa Ziehau #include <dev/hyperv/include/hyperv.h> 49de69dfbbSSepherosa Ziehau #include <dev/hyperv/include/hyperv_busdma.h> 509729f076SSouradeep Chakrabarti #include <dev/hyperv/vmbus/x86/hyperv_machdep.h> 519729f076SSouradeep Chakrabarti #include <dev/hyperv/vmbus/x86/hyperv_reg.h> 52de69dfbbSSepherosa Ziehau #include <dev/hyperv/vmbus/hyperv_var.h> 539729f076SSouradeep Chakrabarti #include <dev/hyperv/vmbus/hyperv_common_reg.h> 54de69dfbbSSepherosa Ziehau 55de69dfbbSSepherosa Ziehau struct hyperv_reftsc_ctx { 56de69dfbbSSepherosa Ziehau struct hyperv_reftsc *tsc_ref; 57de69dfbbSSepherosa Ziehau }; 58de69dfbbSSepherosa Ziehau 59fff5be0bSSepherosa Ziehau static uint32_t hyperv_tsc_vdso_timehands( 60fff5be0bSSepherosa Ziehau struct vdso_timehands *, 61fff5be0bSSepherosa Ziehau struct timecounter *); 62fff5be0bSSepherosa Ziehau 639622c93aSSepherosa Ziehau static d_open_t hyperv_tsc_open; 649622c93aSSepherosa Ziehau static d_mmap_t hyperv_tsc_mmap; 659622c93aSSepherosa Ziehau 66de69dfbbSSepherosa Ziehau static struct timecounter hyperv_tsc_timecounter = { 67de69dfbbSSepherosa Ziehau .tc_get_timecount = NULL, /* based on CPU vendor. */ 68de69dfbbSSepherosa Ziehau .tc_counter_mask = 0xffffffff, 69de69dfbbSSepherosa Ziehau .tc_frequency = HYPERV_TIMER_FREQ, 70de69dfbbSSepherosa Ziehau .tc_name = "Hyper-V-TSC", 71de69dfbbSSepherosa Ziehau .tc_quality = 3000, 72fff5be0bSSepherosa Ziehau .tc_fill_vdso_timehands = hyperv_tsc_vdso_timehands, 73de69dfbbSSepherosa Ziehau }; 74de69dfbbSSepherosa Ziehau 759622c93aSSepherosa Ziehau static struct cdevsw hyperv_tsc_cdevsw = { 769622c93aSSepherosa Ziehau .d_version = D_VERSION, 779622c93aSSepherosa Ziehau .d_open = hyperv_tsc_open, 789622c93aSSepherosa Ziehau .d_mmap = hyperv_tsc_mmap, 799622c93aSSepherosa Ziehau .d_name = HYPERV_REFTSC_DEVNAME 809622c93aSSepherosa Ziehau }; 819622c93aSSepherosa Ziehau 82de69dfbbSSepherosa Ziehau static struct hyperv_reftsc_ctx hyperv_ref_tsc; 83d8bf5168SSepherosa Ziehau 84d8bf5168SSepherosa Ziehau uint64_t 85d8bf5168SSepherosa Ziehau hypercall_md(volatile void *hc_addr, uint64_t in_val, 86d8bf5168SSepherosa Ziehau uint64_t in_paddr, uint64_t out_paddr) 87d8bf5168SSepherosa Ziehau { 88d8bf5168SSepherosa Ziehau uint64_t status; 89d8bf5168SSepherosa Ziehau 90d8bf5168SSepherosa Ziehau __asm__ __volatile__ ("mov %0, %%r8" : : "r" (out_paddr): "r8"); 91d8bf5168SSepherosa Ziehau __asm__ __volatile__ ("call *%3" : "=a" (status) : 92d8bf5168SSepherosa Ziehau "c" (in_val), "d" (in_paddr), "m" (hc_addr)); 93d8bf5168SSepherosa Ziehau return (status); 94d8bf5168SSepherosa Ziehau } 95de69dfbbSSepherosa Ziehau 969622c93aSSepherosa Ziehau static int 979622c93aSSepherosa Ziehau hyperv_tsc_open(struct cdev *dev __unused, int oflags, int devtype __unused, 989622c93aSSepherosa Ziehau struct thread *td __unused) 999622c93aSSepherosa Ziehau { 1009622c93aSSepherosa Ziehau 1019622c93aSSepherosa Ziehau if (oflags & FWRITE) 1029622c93aSSepherosa Ziehau return (EPERM); 1039622c93aSSepherosa Ziehau return (0); 1049622c93aSSepherosa Ziehau } 1059622c93aSSepherosa Ziehau 1069622c93aSSepherosa Ziehau static int 1079622c93aSSepherosa Ziehau hyperv_tsc_mmap(struct cdev *dev __unused, vm_ooffset_t offset, 1089622c93aSSepherosa Ziehau vm_paddr_t *paddr, int nprot __unused, vm_memattr_t *memattr __unused) 1099622c93aSSepherosa Ziehau { 1109622c93aSSepherosa Ziehau 1119622c93aSSepherosa Ziehau KASSERT(hyperv_ref_tsc.tsc_ref != NULL, ("reftsc has not been setup")); 1129622c93aSSepherosa Ziehau 1139622c93aSSepherosa Ziehau /* 1149622c93aSSepherosa Ziehau * NOTE: 1159622c93aSSepherosa Ziehau * 'nprot' does not contain information interested to us; 1169622c93aSSepherosa Ziehau * WR-open is blocked by d_open. 1179622c93aSSepherosa Ziehau */ 1189622c93aSSepherosa Ziehau 1199622c93aSSepherosa Ziehau if (offset != 0) 1209622c93aSSepherosa Ziehau return (EOPNOTSUPP); 1219622c93aSSepherosa Ziehau 122*62f9bcf2SAndrew Turner *paddr = pmap_kextract((vm_offset_t)hyperv_ref_tsc.tsc_ref); 1239622c93aSSepherosa Ziehau return (0); 1249622c93aSSepherosa Ziehau } 1259622c93aSSepherosa Ziehau 126fff5be0bSSepherosa Ziehau static uint32_t 127fff5be0bSSepherosa Ziehau hyperv_tsc_vdso_timehands(struct vdso_timehands *vdso_th, 128fff5be0bSSepherosa Ziehau struct timecounter *tc __unused) 129fff5be0bSSepherosa Ziehau { 130fff5be0bSSepherosa Ziehau 131fff5be0bSSepherosa Ziehau vdso_th->th_algo = VDSO_TH_ALGO_X86_HVTSC; 132fff5be0bSSepherosa Ziehau vdso_th->th_x86_shift = 0; 133fff5be0bSSepherosa Ziehau vdso_th->th_x86_hpet_idx = 0; 134d4b2d303SAdam Fenn vdso_th->th_x86_pvc_last_systime = 0; 135d4b2d303SAdam Fenn vdso_th->th_x86_pvc_stable_mask = 0; 136fff5be0bSSepherosa Ziehau bzero(vdso_th->th_res, sizeof(vdso_th->th_res)); 137fff5be0bSSepherosa Ziehau return (1); 138fff5be0bSSepherosa Ziehau } 139fff5be0bSSepherosa Ziehau 140de69dfbbSSepherosa Ziehau #define HYPERV_TSC_TIMECOUNT(fence) \ 141227bb849SSepherosa Ziehau static uint64_t \ 142227bb849SSepherosa Ziehau hyperv_tc64_tsc_##fence(void) \ 143de69dfbbSSepherosa Ziehau { \ 144de69dfbbSSepherosa Ziehau struct hyperv_reftsc *tsc_ref = hyperv_ref_tsc.tsc_ref; \ 145de69dfbbSSepherosa Ziehau uint32_t seq; \ 146de69dfbbSSepherosa Ziehau \ 147de69dfbbSSepherosa Ziehau while ((seq = atomic_load_acq_int(&tsc_ref->tsc_seq)) != 0) { \ 148de69dfbbSSepherosa Ziehau uint64_t disc, ret, tsc; \ 149de69dfbbSSepherosa Ziehau uint64_t scale = tsc_ref->tsc_scale; \ 150de69dfbbSSepherosa Ziehau int64_t ofs = tsc_ref->tsc_ofs; \ 151de69dfbbSSepherosa Ziehau \ 152de69dfbbSSepherosa Ziehau fence(); \ 153de69dfbbSSepherosa Ziehau tsc = rdtsc(); \ 154de69dfbbSSepherosa Ziehau \ 155de69dfbbSSepherosa Ziehau /* ret = ((tsc * scale) >> 64) + ofs */ \ 156de69dfbbSSepherosa Ziehau __asm__ __volatile__ ("mulq %3" : \ 157de69dfbbSSepherosa Ziehau "=d" (ret), "=a" (disc) : \ 158de69dfbbSSepherosa Ziehau "a" (tsc), "r" (scale)); \ 159de69dfbbSSepherosa Ziehau ret += ofs; \ 160de69dfbbSSepherosa Ziehau \ 161de69dfbbSSepherosa Ziehau atomic_thread_fence_acq(); \ 162de69dfbbSSepherosa Ziehau if (tsc_ref->tsc_seq == seq) \ 163de69dfbbSSepherosa Ziehau return (ret); \ 164de69dfbbSSepherosa Ziehau \ 165de69dfbbSSepherosa Ziehau /* Sequence changed; re-sync. */ \ 166de69dfbbSSepherosa Ziehau } \ 167de69dfbbSSepherosa Ziehau /* Fallback to the generic timecounter, i.e. rdmsr. */ \ 168de69dfbbSSepherosa Ziehau return (rdmsr(MSR_HV_TIME_REF_COUNT)); \ 169de69dfbbSSepherosa Ziehau } \ 170227bb849SSepherosa Ziehau \ 171227bb849SSepherosa Ziehau static u_int \ 172227bb849SSepherosa Ziehau hyperv_tsc_timecount_##fence(struct timecounter *tc __unused) \ 173227bb849SSepherosa Ziehau { \ 174227bb849SSepherosa Ziehau \ 175227bb849SSepherosa Ziehau return (hyperv_tc64_tsc_##fence()); \ 176227bb849SSepherosa Ziehau } \ 177de69dfbbSSepherosa Ziehau struct __hack 178de69dfbbSSepherosa Ziehau 179de69dfbbSSepherosa Ziehau HYPERV_TSC_TIMECOUNT(lfence); 180de69dfbbSSepherosa Ziehau HYPERV_TSC_TIMECOUNT(mfence); 181de69dfbbSSepherosa Ziehau 182de69dfbbSSepherosa Ziehau static void 183de69dfbbSSepherosa Ziehau hyperv_tsc_tcinit(void *dummy __unused) 184de69dfbbSSepherosa Ziehau { 185227bb849SSepherosa Ziehau hyperv_tc64_t tc64 = NULL; 186de69dfbbSSepherosa Ziehau uint64_t val, orig; 187de69dfbbSSepherosa Ziehau 188de69dfbbSSepherosa Ziehau if ((hyperv_features & 189de69dfbbSSepherosa Ziehau (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC)) != 190de69dfbbSSepherosa Ziehau (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC) || 191de69dfbbSSepherosa Ziehau (cpu_feature & CPUID_SSE2) == 0) /* SSE2 for mfence/lfence */ 192de69dfbbSSepherosa Ziehau return; 193de69dfbbSSepherosa Ziehau 194de69dfbbSSepherosa Ziehau switch (cpu_vendor_id) { 195de69dfbbSSepherosa Ziehau case CPU_VENDOR_AMD: 1964e012582SKonstantin Belousov case CPU_VENDOR_HYGON: 197de69dfbbSSepherosa Ziehau hyperv_tsc_timecounter.tc_get_timecount = 198de69dfbbSSepherosa Ziehau hyperv_tsc_timecount_mfence; 199227bb849SSepherosa Ziehau tc64 = hyperv_tc64_tsc_mfence; 200de69dfbbSSepherosa Ziehau break; 201de69dfbbSSepherosa Ziehau 202de69dfbbSSepherosa Ziehau case CPU_VENDOR_INTEL: 203de69dfbbSSepherosa Ziehau hyperv_tsc_timecounter.tc_get_timecount = 204de69dfbbSSepherosa Ziehau hyperv_tsc_timecount_lfence; 205227bb849SSepherosa Ziehau tc64 = hyperv_tc64_tsc_lfence; 206de69dfbbSSepherosa Ziehau break; 207de69dfbbSSepherosa Ziehau 208de69dfbbSSepherosa Ziehau default: 209630c5177SGordon Bergling /* Unsupported CPU vendors. */ 210de69dfbbSSepherosa Ziehau return; 211de69dfbbSSepherosa Ziehau } 212de69dfbbSSepherosa Ziehau 213*62f9bcf2SAndrew Turner hyperv_ref_tsc.tsc_ref = contigmalloc(PAGE_SIZE, M_DEVBUF, 214*62f9bcf2SAndrew Turner M_WAITOK | M_ZERO, 0ul, ~0ul, PAGE_SIZE, 0); 215de69dfbbSSepherosa Ziehau if (hyperv_ref_tsc.tsc_ref == NULL) { 216de69dfbbSSepherosa Ziehau printf("hyperv: reftsc page allocation failed\n"); 217de69dfbbSSepherosa Ziehau return; 218de69dfbbSSepherosa Ziehau } 219de69dfbbSSepherosa Ziehau 220de69dfbbSSepherosa Ziehau orig = rdmsr(MSR_HV_REFERENCE_TSC); 221*62f9bcf2SAndrew Turner val = (pmap_kextract((vm_offset_t)hyperv_ref_tsc.tsc_ref) >> 222*62f9bcf2SAndrew Turner PAGE_SHIFT) << MSR_HV_REFTSC_PGSHIFT; 223*62f9bcf2SAndrew Turner val |= MSR_HV_REFTSC_ENABLE | (orig & MSR_HV_REFTSC_RSVD_MASK); 224de69dfbbSSepherosa Ziehau wrmsr(MSR_HV_REFERENCE_TSC, val); 225de69dfbbSSepherosa Ziehau 226de69dfbbSSepherosa Ziehau /* Register "enlightened" timecounter. */ 227de69dfbbSSepherosa Ziehau tc_init(&hyperv_tsc_timecounter); 2289622c93aSSepherosa Ziehau 229227bb849SSepherosa Ziehau /* Install 64 bits timecounter method for other modules to use. */ 230227bb849SSepherosa Ziehau KASSERT(tc64 != NULL, ("tc64 is not set")); 231227bb849SSepherosa Ziehau hyperv_tc64 = tc64; 232227bb849SSepherosa Ziehau 2339622c93aSSepherosa Ziehau /* Add device for mmap(2). */ 2349622c93aSSepherosa Ziehau make_dev(&hyperv_tsc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0444, 2359622c93aSSepherosa Ziehau HYPERV_REFTSC_DEVNAME); 236de69dfbbSSepherosa Ziehau } 237de69dfbbSSepherosa Ziehau SYSINIT(hyperv_tsc_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, hyperv_tsc_tcinit, 238de69dfbbSSepherosa Ziehau NULL); 239