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);
Cache object: deb77871a459315cdfdb385073da1abb
|