2 * Copyright (c) 2016 Microsoft Corp.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
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.
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.
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
32 #include <sys/fcntl.h>
33 #include <sys/kernel.h>
34 #include <sys/systm.h>
35 #include <sys/timetc.h>
37 #include <machine/cpufunc.h>
38 #include <machine/cputypes.h>
39 #include <machine/md_var.h>
40 #include <machine/specialreg.h>
44 #include <dev/hyperv/include/hyperv.h>
45 #include <dev/hyperv/include/hyperv_busdma.h>
46 #include <dev/hyperv/vmbus/hyperv_machdep.h>
47 #include <dev/hyperv/vmbus/hyperv_reg.h>
48 #include <dev/hyperv/vmbus/hyperv_var.h>
50 struct hyperv_reftsc_ctx {
51 struct hyperv_reftsc *tsc_ref;
52 struct hyperv_dma tsc_ref_dma;
55 static d_open_t hyperv_tsc_open;
56 static d_mmap_t hyperv_tsc_mmap;
58 static struct timecounter hyperv_tsc_timecounter = {
59 .tc_get_timecount = NULL, /* based on CPU vendor. */
61 .tc_counter_mask = 0xffffffff,
62 .tc_frequency = HYPERV_TIMER_FREQ,
63 .tc_name = "Hyper-V-TSC",
69 static struct cdevsw hyperv_tsc_cdevsw = {
70 .d_version = D_VERSION,
71 .d_open = hyperv_tsc_open,
72 .d_mmap = hyperv_tsc_mmap,
73 .d_name = HYPERV_REFTSC_DEVNAME
76 static struct hyperv_reftsc_ctx hyperv_ref_tsc;
79 hypercall_md(volatile void *hc_addr, uint64_t in_val,
80 uint64_t in_paddr, uint64_t out_paddr)
84 __asm__ __volatile__ ("mov %0, %%r8" : : "r" (out_paddr): "r8");
85 __asm__ __volatile__ ("call *%3" : "=a" (status) :
86 "c" (in_val), "d" (in_paddr), "m" (hc_addr));
91 hyperv_tsc_open(struct cdev *dev __unused, int oflags, int devtype __unused,
92 struct thread *td __unused)
101 hyperv_tsc_mmap(struct cdev *dev __unused, vm_ooffset_t offset,
102 vm_paddr_t *paddr, int nprot __unused, vm_memattr_t *memattr __unused)
105 KASSERT(hyperv_ref_tsc.tsc_ref != NULL, ("reftsc has not been setup"));
109 * 'nprot' does not contain information interested to us;
110 * WR-open is blocked by d_open.
116 *paddr = hyperv_ref_tsc.tsc_ref_dma.hv_paddr;
120 #define HYPERV_TSC_TIMECOUNT(fence) \
122 hyperv_tsc_timecount_##fence(struct timecounter *tc) \
124 struct hyperv_reftsc *tsc_ref = hyperv_ref_tsc.tsc_ref; \
127 while ((seq = tsc_ref->tsc_seq) != 0) { \
128 uint64_t disc, ret, tsc, scale; \
131 __compiler_membar(); \
132 scale = tsc_ref->tsc_scale; \
133 ofs = tsc_ref->tsc_ofs; \
138 /* ret = ((tsc * scale) >> 64) + ofs */ \
139 __asm__ __volatile__ ("mulq %3" : \
140 "=d" (ret), "=a" (disc) : \
141 "a" (tsc), "r" (scale)); \
144 __compiler_membar(); \
145 if (tsc_ref->tsc_seq == seq) \
148 /* Sequence changed; re-sync. */ \
150 /* Fallback to the generic timecounter, i.e. rdmsr. */ \
151 return (rdmsr(MSR_HV_TIME_REF_COUNT)); \
155 HYPERV_TSC_TIMECOUNT(lfence);
156 HYPERV_TSC_TIMECOUNT(mfence);
159 hyperv_tsc_tcinit(void *dummy __unused)
163 if ((hyperv_features &
164 (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC)) !=
165 (CPUID_HV_MSR_TIME_REFCNT | CPUID_HV_MSR_REFERENCE_TSC) ||
166 (cpu_feature & CPUID_SSE2) == 0) /* SSE2 for mfence/lfence */
169 switch (cpu_vendor_id) {
171 hyperv_tsc_timecounter.tc_get_timecount =
172 hyperv_tsc_timecount_mfence;
175 case CPU_VENDOR_INTEL:
176 hyperv_tsc_timecounter.tc_get_timecount =
177 hyperv_tsc_timecount_lfence;
181 /* Unsupport CPU vendors. */
185 hyperv_ref_tsc.tsc_ref = hyperv_dmamem_alloc(NULL, PAGE_SIZE, 0,
186 sizeof(struct hyperv_reftsc), &hyperv_ref_tsc.tsc_ref_dma,
187 BUS_DMA_WAITOK | BUS_DMA_ZERO);
188 if (hyperv_ref_tsc.tsc_ref == NULL) {
189 printf("hyperv: reftsc page allocation failed\n");
193 orig = rdmsr(MSR_HV_REFERENCE_TSC);
194 val = MSR_HV_REFTSC_ENABLE | (orig & MSR_HV_REFTSC_RSVD_MASK) |
195 ((hyperv_ref_tsc.tsc_ref_dma.hv_paddr >> PAGE_SHIFT) <<
196 MSR_HV_REFTSC_PGSHIFT);
197 wrmsr(MSR_HV_REFERENCE_TSC, val);
199 /* Register "enlightened" timecounter. */
200 tc_init(&hyperv_tsc_timecounter);
202 /* Add device for mmap(2). */
203 make_dev(&hyperv_tsc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0444,
204 HYPERV_REFTSC_DEVNAME);
206 SYSINIT(hyperv_tsc_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, hyperv_tsc_tcinit,