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/param.h>
28 #include <sys/systimer.h>
29 #include <sys/systm.h>
31 #include <machine/cpufunc.h>
32 #include <machine/specialreg.h>
33 #include <machine/msi_machdep.h>
35 #include <dev/virtual/hyperv/hyperv_busdma.h>
36 #include <dev/virtual/hyperv/vmbus/hyperv_reg.h>
37 #include <dev/virtual/hyperv/vmbus/hyperv_var.h>
38 #include <dev/virtual/hyperv/vmbus/hyperv_machdep.h>
40 struct hyperv_reftsc_ctx
{
41 struct hyperv_reftsc
*tsc_ref
;
42 struct hyperv_dma tsc_ref_dma
;
45 static void hyperv_tsc_cputimer_construct(struct cputimer
*,
47 static sysclock_t
hyperv_tsc_cputimer_count_mfence(void);
48 static sysclock_t
hyperv_tsc_cputimer_count_lfence(void);
50 static struct hyperv_reftsc_ctx hyperv_ref_tsc
;
51 static hyperv_tc64_t hyperv_tc64_saved
;
53 static struct cputimer hyperv_tsc_cputimer
= {
54 .next
= SLIST_ENTRY_INITIALIZER
,
55 .name
= "Hyper-V-TSC",
56 .pri
= CPUTIMER_PRI_VMM_HI
,
57 .type
= CPUTIMER_VMM1
,
58 .count
= NULL
, /* determined later */
59 .fromhz
= cputimer_default_fromhz
,
60 .fromus
= cputimer_default_fromus
,
61 .construct
= hyperv_tsc_cputimer_construct
,
62 .destruct
= cputimer_default_destruct
,
63 .freq
= HYPERV_TIMER_FREQ
67 hypercall_md(volatile void *hc_addr
, uint64_t in_val
,
68 uint64_t in_paddr
, uint64_t out_paddr
)
72 __asm__
__volatile__ ("mov %0, %%r8" : : "r" (out_paddr
): "r8");
73 __asm__
__volatile__ ("call *%3" : "=a" (status
) :
74 "c" (in_val
), "d" (in_paddr
), "m" (hc_addr
));
79 hyperv_msi2vector(uint64_t msi_addr __unused
, uint32_t msi_data
)
81 return (msi_data
& MSI_X86_DATA_INTVEC
);
84 #define HYPERV_TSC(fence) \
86 hyperv_tsc_##fence(void) \
88 struct hyperv_reftsc *tsc_ref = hyperv_ref_tsc.tsc_ref; \
91 while ((seq = tsc_ref->tsc_seq) != 0) { \
92 uint64_t disc, ret, tsc; \
97 scale = tsc_ref->tsc_scale; \
98 ofs = tsc_ref->tsc_ofs; \
103 /* ret = ((tsc * scale) >> 64) + ofs */ \
104 __asm__ __volatile__ ("mulq %3" : \
105 "=d" (ret), "=a" (disc) : \
106 "a" (tsc), "r" (scale)); \
110 if (tsc_ref->tsc_seq == seq) \
113 /* Sequence changed; re-sync. */ \
115 /* Fallback to the generic rdmsr. */ \
116 return (rdmsr(MSR_HV_TIME_REF_COUNT)); \
124 hyperv_tsc_cputimer_count_lfence(void)
128 val
= hyperv_tsc_lfence();
129 return (val
+ hyperv_tsc_cputimer
.base
);
133 hyperv_tsc_cputimer_count_mfence(void)
137 val
= hyperv_tsc_mfence();
138 return (val
+ hyperv_tsc_cputimer
.base
);
142 hyperv_tsc_cputimer_construct(struct cputimer
*timer
, sysclock_t oldclock
)
145 timer
->base
= oldclock
- timer
->count();
151 hyperv_tc64_t tc64
= NULL
;
154 if ((hyperv_features
&
155 (CPUID_HV_MSR_TIME_REFCNT
| CPUID_HV_MSR_REFERENCE_TSC
)) !=
156 (CPUID_HV_MSR_TIME_REFCNT
| CPUID_HV_MSR_REFERENCE_TSC
) ||
157 (cpu_feature
& CPUID_SSE2
) == 0) /* SSE2 for mfence/lfence */
160 switch (cpu_vendor_id
) {
162 hyperv_tsc_cputimer
.count
= hyperv_tsc_cputimer_count_mfence
;
163 tc64
= hyperv_tsc_mfence
;
166 case CPU_VENDOR_INTEL
:
167 hyperv_tsc_cputimer
.count
= hyperv_tsc_cputimer_count_lfence
;
168 tc64
= hyperv_tsc_lfence
;
172 /* Unsupport CPU vendors. */
176 hyperv_ref_tsc
.tsc_ref
= hyperv_dmamem_alloc(NULL
, PAGE_SIZE
, 0,
177 sizeof(struct hyperv_reftsc
), &hyperv_ref_tsc
.tsc_ref_dma
,
178 BUS_DMA_WAITOK
| BUS_DMA_ZERO
);
179 if (hyperv_ref_tsc
.tsc_ref
== NULL
) {
180 kprintf("hyperv: reftsc page allocation failed\n");
184 orig
= rdmsr(MSR_HV_REFERENCE_TSC
);
185 val
= MSR_HV_REFTSC_ENABLE
| (orig
& MSR_HV_REFTSC_RSVD_MASK
) |
186 ((hyperv_ref_tsc
.tsc_ref_dma
.hv_paddr
>> PAGE_SHIFT
) <<
187 MSR_HV_REFTSC_PGSHIFT
);
188 wrmsr(MSR_HV_REFERENCE_TSC
, val
);
190 /* Register Hyper-V reference TSC systimer. */
191 cputimer_register(&hyperv_tsc_cputimer
);
192 cputimer_select(&hyperv_tsc_cputimer
, 0);
193 KASSERT(tc64
!= NULL
, ("tc64 is not set"));
194 hyperv_tc64_saved
= hyperv_tc64
;
199 hyperv_md_uninit(void)
201 if (hyperv_ref_tsc
.tsc_ref
!= NULL
) {
204 /* Deregister Hyper-V reference TSC systimer. */
205 cputimer_deregister(&hyperv_tsc_cputimer
);
206 /* Revert tc64 change. */
207 hyperv_tc64
= hyperv_tc64_saved
;
209 val
= rdmsr(MSR_HV_REFERENCE_TSC
);
210 wrmsr(MSR_HV_REFERENCE_TSC
, val
& MSR_HV_REFTSC_RSVD_MASK
);
212 hyperv_dmamem_free(&hyperv_ref_tsc
.tsc_ref_dma
,
213 hyperv_ref_tsc
.tsc_ref
);
214 hyperv_ref_tsc
.tsc_ref
= NULL
;