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/param.h>
289622c93aSSepherosa Ziehau #include <sys/conf.h>
299622c93aSSepherosa Ziehau #include <sys/fcntl.h>
30de69dfbbSSepherosa Ziehau #include <sys/kernel.h>
31*62f9bcf2SAndrew Turner #include <sys/malloc.h>
32de69dfbbSSepherosa Ziehau #include <sys/systm.h>
33de69dfbbSSepherosa Ziehau #include <sys/timetc.h>
34fff5be0bSSepherosa Ziehau #include <sys/vdso.h>
35de69dfbbSSepherosa Ziehau
36de69dfbbSSepherosa Ziehau #include <machine/cpufunc.h>
37de69dfbbSSepherosa Ziehau #include <machine/cputypes.h>
38de69dfbbSSepherosa Ziehau #include <machine/md_var.h>
399622c93aSSepherosa Ziehau #include <machine/specialreg.h>
409622c93aSSepherosa Ziehau
419622c93aSSepherosa Ziehau #include <vm/vm.h>
42*62f9bcf2SAndrew Turner #include <vm/vm_extern.h>
43*62f9bcf2SAndrew Turner #include <vm/pmap.h>
44de69dfbbSSepherosa Ziehau
45de69dfbbSSepherosa Ziehau #include <dev/hyperv/include/hyperv.h>
46de69dfbbSSepherosa Ziehau #include <dev/hyperv/include/hyperv_busdma.h>
479729f076SSouradeep Chakrabarti #include <dev/hyperv/vmbus/x86/hyperv_machdep.h>
489729f076SSouradeep Chakrabarti #include <dev/hyperv/vmbus/x86/hyperv_reg.h>
49de69dfbbSSepherosa Ziehau #include <dev/hyperv/vmbus/hyperv_var.h>
509729f076SSouradeep Chakrabarti #include <dev/hyperv/vmbus/hyperv_common_reg.h>
51de69dfbbSSepherosa Ziehau
52de69dfbbSSepherosa Ziehau struct hyperv_reftsc_ctx {
53de69dfbbSSepherosa Ziehau struct hyperv_reftsc *tsc_ref;
54de69dfbbSSepherosa Ziehau };
55de69dfbbSSepherosa Ziehau
56fff5be0bSSepherosa Ziehau static uint32_t hyperv_tsc_vdso_timehands(
57fff5be0bSSepherosa Ziehau struct vdso_timehands *,
58fff5be0bSSepherosa Ziehau struct timecounter *);
59fff5be0bSSepherosa Ziehau
609622c93aSSepherosa Ziehau static d_open_t hyperv_tsc_open;
619622c93aSSepherosa Ziehau static d_mmap_t hyperv_tsc_mmap;
629622c93aSSepherosa Ziehau
63de69dfbbSSepherosa Ziehau static struct timecounter hyperv_tsc_timecounter = {
64de69dfbbSSepherosa Ziehau .tc_get_timecount = NULL, /* based on CPU vendor. */
65de69dfbbSSepherosa Ziehau .tc_counter_mask = 0xffffffff,
66de69dfbbSSepherosa Ziehau .tc_frequency = HYPERV_TIMER_FREQ,
67de69dfbbSSepherosa Ziehau .tc_name = "Hyper-V-TSC",
68de69dfbbSSepherosa Ziehau .tc_quality = 3000,
69fff5be0bSSepherosa Ziehau .tc_fill_vdso_timehands = hyperv_tsc_vdso_timehands,
70de69dfbbSSepherosa Ziehau };
71de69dfbbSSepherosa Ziehau
729622c93aSSepherosa Ziehau static struct cdevsw hyperv_tsc_cdevsw = {
739622c93aSSepherosa Ziehau .d_version = D_VERSION,
749622c93aSSepherosa Ziehau .d_open = hyperv_tsc_open,
759622c93aSSepherosa Ziehau .d_mmap = hyperv_tsc_mmap,
769622c93aSSepherosa Ziehau .d_name = HYPERV_REFTSC_DEVNAME
779622c93aSSepherosa Ziehau };
789622c93aSSepherosa Ziehau
79de69dfbbSSepherosa Ziehau static struct hyperv_reftsc_ctx hyperv_ref_tsc;
80d8bf5168SSepherosa Ziehau
81d8bf5168SSepherosa Ziehau uint64_t
hypercall_md(volatile void * hc_addr,uint64_t in_val,uint64_t in_paddr,uint64_t out_paddr)82d8bf5168SSepherosa Ziehau hypercall_md(volatile void *hc_addr, uint64_t in_val,
83d8bf5168SSepherosa Ziehau uint64_t in_paddr, uint64_t out_paddr)
84d8bf5168SSepherosa Ziehau {
85d8bf5168SSepherosa Ziehau uint64_t status;
86d8bf5168SSepherosa Ziehau
87d8bf5168SSepherosa Ziehau __asm__ __volatile__ ("mov %0, %%r8" : : "r" (out_paddr): "r8");
88d8bf5168SSepherosa Ziehau __asm__ __volatile__ ("call *%3" : "=a" (status) :
89d8bf5168SSepherosa Ziehau "c" (in_val), "d" (in_paddr), "m" (hc_addr));
90d8bf5168SSepherosa Ziehau return (status);
91d8bf5168SSepherosa Ziehau }
92de69dfbbSSepherosa Ziehau
939622c93aSSepherosa Ziehau static int
hyperv_tsc_open(struct cdev * dev __unused,int oflags,int devtype __unused,struct thread * td __unused)949622c93aSSepherosa Ziehau hyperv_tsc_open(struct cdev *dev __unused, int oflags, int devtype __unused,
959622c93aSSepherosa Ziehau struct thread *td __unused)
969622c93aSSepherosa Ziehau {
979622c93aSSepherosa Ziehau
989622c93aSSepherosa Ziehau if (oflags & FWRITE)
999622c93aSSepherosa Ziehau return (EPERM);
1009622c93aSSepherosa Ziehau return (0);
1019622c93aSSepherosa Ziehau }
1029622c93aSSepherosa Ziehau
1039622c93aSSepherosa Ziehau static int
hyperv_tsc_mmap(struct cdev * dev __unused,vm_ooffset_t offset,vm_paddr_t * paddr,int nprot __unused,vm_memattr_t * memattr __unused)1049622c93aSSepherosa Ziehau hyperv_tsc_mmap(struct cdev *dev __unused, vm_ooffset_t offset,
1059622c93aSSepherosa Ziehau vm_paddr_t *paddr, int nprot __unused, vm_memattr_t *memattr __unused)
1069622c93aSSepherosa Ziehau {
1079622c93aSSepherosa Ziehau
1089622c93aSSepherosa Ziehau KASSERT(hyperv_ref_tsc.tsc_ref != NULL, ("reftsc has not been setup"));
1099622c93aSSepherosa Ziehau
1109622c93aSSepherosa Ziehau /*
1119622c93aSSepherosa Ziehau * NOTE:
1129622c93aSSepherosa Ziehau * 'nprot' does not contain information interested to us;
1139622c93aSSepherosa Ziehau * WR-open is blocked by d_open.
1149622c93aSSepherosa Ziehau */
1159622c93aSSepherosa Ziehau
1169622c93aSSepherosa Ziehau if (offset != 0)
1179622c93aSSepherosa Ziehau return (EOPNOTSUPP);
1189622c93aSSepherosa Ziehau
119*62f9bcf2SAndrew Turner *paddr = pmap_kextract((vm_offset_t)hyperv_ref_tsc.tsc_ref);
1209622c93aSSepherosa Ziehau return (0);
1219622c93aSSepherosa Ziehau }
1229622c93aSSepherosa Ziehau
123fff5be0bSSepherosa Ziehau static uint32_t
hyperv_tsc_vdso_timehands(struct vdso_timehands * vdso_th,struct timecounter * tc __unused)124fff5be0bSSepherosa Ziehau hyperv_tsc_vdso_timehands(struct vdso_timehands *vdso_th,
125fff5be0bSSepherosa Ziehau struct timecounter *tc __unused)
126fff5be0bSSepherosa Ziehau {
127fff5be0bSSepherosa Ziehau
128fff5be0bSSepherosa Ziehau vdso_th->th_algo = VDSO_TH_ALGO_X86_HVTSC;
129fff5be0bSSepherosa Ziehau vdso_th->th_x86_shift = 0;
130fff5be0bSSepherosa Ziehau vdso_th->th_x86_hpet_idx = 0;
131d4b2d303SAdam Fenn vdso_th->th_x86_pvc_last_systime = 0;
132d4b2d303SAdam Fenn vdso_th->th_x86_pvc_stable_mask = 0;
133fff5be0bSSepherosa Ziehau bzero(vdso_th->th_res, sizeof(vdso_th->th_res));
134fff5be0bSSepherosa Ziehau return (1);
135fff5be0bSSepherosa Ziehau }
136fff5be0bSSepherosa Ziehau
137de69dfbbSSepherosa Ziehau #define HYPERV_TSC_TIMECOUNT(fence) \
138227bb849SSepherosa Ziehau static uint64_t \
139227bb849SSepherosa Ziehau hyperv_tc64_tsc_##fence(void) \
140de69dfbbSSepherosa Ziehau { \
141de69dfbbSSepherosa Ziehau struct hyperv_reftsc *tsc_ref = hyperv_ref_tsc.tsc_ref; \
142de69dfbbSSepherosa Ziehau uint32_t seq; \
143de69dfbbSSepherosa Ziehau \
144de69dfbbSSepherosa Ziehau while ((seq = atomic_load_acq_int(&tsc_ref->tsc_seq)) != 0) { \
145de69dfbbSSepherosa Ziehau uint64_t disc, ret, tsc; \
146de69dfbbSSepherosa Ziehau uint64_t scale = tsc_ref->tsc_scale; \
147de69dfbbSSepherosa Ziehau int64_t ofs = tsc_ref->tsc_ofs; \
148de69dfbbSSepherosa Ziehau \
149de69dfbbSSepherosa Ziehau fence(); \
150de69dfbbSSepherosa Ziehau tsc = rdtsc(); \
151de69dfbbSSepherosa Ziehau \
152de69dfbbSSepherosa Ziehau /* ret = ((tsc * scale) >> 64) + ofs */ \
153de69dfbbSSepherosa Ziehau __asm__ __volatile__ ("mulq %3" : \
154de69dfbbSSepherosa Ziehau "=d" (ret), "=a" (disc) : \
155de69dfbbSSepherosa Ziehau "a" (tsc), "r" (scale)); \
156de69dfbbSSepherosa Ziehau ret += ofs; \
157de69dfbbSSepherosa Ziehau \
158de69dfbbSSepherosa Ziehau atomic_thread_fence_acq(); \
159de69dfbbSSepherosa Ziehau if (tsc_ref->tsc_seq == seq) \
160de69dfbbSSepherosa Ziehau return (ret); \
161de69dfbbSSepherosa Ziehau \
162de69dfbbSSepherosa Ziehau /* Sequence changed; re-sync. */ \
163de69dfbbSSepherosa Ziehau } \
164de69dfbbSSepherosa Ziehau /* Fallback to the generic timecounter, i.e. rdmsr. */ \
165de69dfbbSSepherosa Ziehau return (rdmsr(MSR_HV_TIME_REF_COUNT)); \
166de69dfbbSSepherosa Ziehau } \
167227bb849SSepherosa Ziehau \
168227bb849SSepherosa Ziehau static u_int \
169227bb849SSepherosa Ziehau hyperv_tsc_timecount_##fence(struct timecounter *tc __unused) \
170227bb849SSepherosa Ziehau { \
171227bb849SSepherosa Ziehau \
172227bb849SSepherosa Ziehau return (hyperv_tc64_tsc_##fence()); \
173227bb849SSepherosa Ziehau } \
174de69dfbbSSepherosa Ziehau struct __hack
175de69dfbbSSepherosa Ziehau
176de69dfbbSSepherosa Ziehau HYPERV_TSC_TIMECOUNT(lfence);
177de69dfbbSSepherosa Ziehau HYPERV_TSC_TIMECOUNT(mfence);
178de69dfbbSSepherosa Ziehau
179de69dfbbSSepherosa Ziehau static void
hyperv_tsc_tcinit(void * dummy __unused)180de69dfbbSSepherosa Ziehau hyperv_tsc_tcinit(void *dummy __unused)
181de69dfbbSSepherosa Ziehau {
182227bb849SSepherosa Ziehau hyperv_tc64_t tc64 = NULL;
183de69dfbbSSepherosa Ziehau uint64_t val, orig;
184de69dfbbSSepherosa Ziehau
185de69dfbbSSepherosa Ziehau if ((hyperv_features &
186de69dfbbSSepherosa Ziehau (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC)) !=
187de69dfbbSSepherosa Ziehau (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC) ||
188de69dfbbSSepherosa Ziehau (cpu_feature & CPUID_SSE2) == 0) /* SSE2 for mfence/lfence */
189de69dfbbSSepherosa Ziehau return;
190de69dfbbSSepherosa Ziehau
191de69dfbbSSepherosa Ziehau switch (cpu_vendor_id) {
192de69dfbbSSepherosa Ziehau case CPU_VENDOR_AMD:
1934e012582SKonstantin Belousov case CPU_VENDOR_HYGON:
194de69dfbbSSepherosa Ziehau hyperv_tsc_timecounter.tc_get_timecount =
195de69dfbbSSepherosa Ziehau hyperv_tsc_timecount_mfence;
196227bb849SSepherosa Ziehau tc64 = hyperv_tc64_tsc_mfence;
197de69dfbbSSepherosa Ziehau break;
198de69dfbbSSepherosa Ziehau
199de69dfbbSSepherosa Ziehau case CPU_VENDOR_INTEL:
200de69dfbbSSepherosa Ziehau hyperv_tsc_timecounter.tc_get_timecount =
201de69dfbbSSepherosa Ziehau hyperv_tsc_timecount_lfence;
202227bb849SSepherosa Ziehau tc64 = hyperv_tc64_tsc_lfence;
203de69dfbbSSepherosa Ziehau break;
204de69dfbbSSepherosa Ziehau
205de69dfbbSSepherosa Ziehau default:
206630c5177SGordon Bergling /* Unsupported CPU vendors. */
207de69dfbbSSepherosa Ziehau return;
208de69dfbbSSepherosa Ziehau }
209de69dfbbSSepherosa Ziehau
210*62f9bcf2SAndrew Turner hyperv_ref_tsc.tsc_ref = contigmalloc(PAGE_SIZE, M_DEVBUF,
211*62f9bcf2SAndrew Turner M_WAITOK | M_ZERO, 0ul, ~0ul, PAGE_SIZE, 0);
212de69dfbbSSepherosa Ziehau if (hyperv_ref_tsc.tsc_ref == NULL) {
213de69dfbbSSepherosa Ziehau printf("hyperv: reftsc page allocation failed\n");
214de69dfbbSSepherosa Ziehau return;
215de69dfbbSSepherosa Ziehau }
216de69dfbbSSepherosa Ziehau
217de69dfbbSSepherosa Ziehau orig = rdmsr(MSR_HV_REFERENCE_TSC);
218*62f9bcf2SAndrew Turner val = (pmap_kextract((vm_offset_t)hyperv_ref_tsc.tsc_ref) >>
219*62f9bcf2SAndrew Turner PAGE_SHIFT) << MSR_HV_REFTSC_PGSHIFT;
220*62f9bcf2SAndrew Turner val |= MSR_HV_REFTSC_ENABLE | (orig & MSR_HV_REFTSC_RSVD_MASK);
221de69dfbbSSepherosa Ziehau wrmsr(MSR_HV_REFERENCE_TSC, val);
222de69dfbbSSepherosa Ziehau
223de69dfbbSSepherosa Ziehau /* Register "enlightened" timecounter. */
224de69dfbbSSepherosa Ziehau tc_init(&hyperv_tsc_timecounter);
2259622c93aSSepherosa Ziehau
226227bb849SSepherosa Ziehau /* Install 64 bits timecounter method for other modules to use. */
227227bb849SSepherosa Ziehau KASSERT(tc64 != NULL, ("tc64 is not set"));
228227bb849SSepherosa Ziehau hyperv_tc64 = tc64;
229227bb849SSepherosa Ziehau
2309622c93aSSepherosa Ziehau /* Add device for mmap(2). */
2319622c93aSSepherosa Ziehau make_dev(&hyperv_tsc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0444,
2329622c93aSSepherosa Ziehau HYPERV_REFTSC_DEVNAME);
233de69dfbbSSepherosa Ziehau }
234de69dfbbSSepherosa Ziehau SYSINIT(hyperv_tsc_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, hyperv_tsc_tcinit,
235de69dfbbSSepherosa Ziehau NULL);
236