xref: /freebsd/sys/dev/hyperv/vmbus/amd64/hyperv_machdep.c (revision 62f9bcf2b4271d58ccf0bd8a81c540bb99a53ef7)
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