]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/vmbus/amd64/hyperv_machdep.c
MFC 310048,310101
[FreeBSD/stable/10.git] / sys / dev / hyperv / vmbus / amd64 / hyperv_machdep.c
1 /*-
2  * Copyright (c) 2016 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
37 #include <machine/cpufunc.h>
38 #include <machine/cputypes.h>
39 #include <machine/md_var.h>
40 #include <machine/specialreg.h>
41
42 #include <vm/vm.h>
43
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>
49
50 struct hyperv_reftsc_ctx {
51         struct hyperv_reftsc    *tsc_ref;
52         struct hyperv_dma       tsc_ref_dma;
53 };
54
55 static d_open_t                 hyperv_tsc_open;
56 static d_mmap_t                 hyperv_tsc_mmap;
57
58 static struct timecounter       hyperv_tsc_timecounter = {
59         .tc_get_timecount       = NULL, /* based on CPU vendor. */
60         .tc_poll_pps            = NULL,
61         .tc_counter_mask        = 0xffffffff,
62         .tc_frequency           = HYPERV_TIMER_FREQ,
63         .tc_name                = "Hyper-V-TSC",
64         .tc_quality             = 3000,
65         .tc_flags               = 0,
66         .tc_priv                = NULL
67 };
68
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
74 };
75
76 static struct hyperv_reftsc_ctx hyperv_ref_tsc;
77
78 uint64_t
79 hypercall_md(volatile void *hc_addr, uint64_t in_val,
80     uint64_t in_paddr, uint64_t out_paddr)
81 {
82         uint64_t status;
83
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));
87         return (status);
88 }
89
90 static int
91 hyperv_tsc_open(struct cdev *dev __unused, int oflags, int devtype __unused,
92     struct thread *td __unused)
93 {
94
95         if (oflags & FWRITE)
96                 return (EPERM);
97         return (0);
98 }
99
100 static int
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)
103 {
104
105         KASSERT(hyperv_ref_tsc.tsc_ref != NULL, ("reftsc has not been setup"));
106
107         /*
108          * NOTE:
109          * 'nprot' does not contain information interested to us;
110          * WR-open is blocked by d_open.
111          */
112
113         if (offset != 0)
114                 return (EOPNOTSUPP);
115
116         *paddr = hyperv_ref_tsc.tsc_ref_dma.hv_paddr;
117         return (0);
118 }
119
120 #define HYPERV_TSC_TIMECOUNT(fence)                                     \
121 static u_int                                                            \
122 hyperv_tsc_timecount_##fence(struct timecounter *tc)                    \
123 {                                                                       \
124         struct hyperv_reftsc *tsc_ref = hyperv_ref_tsc.tsc_ref;         \
125         uint32_t seq;                                                   \
126                                                                         \
127         while ((seq = tsc_ref->tsc_seq) != 0) {                         \
128                 uint64_t disc, ret, tsc, scale;                         \
129                 int64_t ofs;                                            \
130                                                                         \
131                 __compiler_membar();                                    \
132                 scale = tsc_ref->tsc_scale;                             \
133                 ofs = tsc_ref->tsc_ofs;                                 \
134                                                                         \
135                 fence();                                                \
136                 tsc = rdtsc();                                          \
137                                                                         \
138                 /* ret = ((tsc * scale) >> 64) + ofs */                 \
139                 __asm__ __volatile__ ("mulq %3" :                       \
140                     "=d" (ret), "=a" (disc) :                           \
141                     "a" (tsc), "r" (scale));                            \
142                 ret += ofs;                                             \
143                                                                         \
144                 __compiler_membar();                                    \
145                 if (tsc_ref->tsc_seq == seq)                            \
146                         return (ret);                                   \
147                                                                         \
148                 /* Sequence changed; re-sync. */                        \
149         }                                                               \
150         /* Fallback to the generic timecounter, i.e. rdmsr. */          \
151         return (rdmsr(MSR_HV_TIME_REF_COUNT));                          \
152 }                                                                       \
153 struct __hack
154
155 HYPERV_TSC_TIMECOUNT(lfence);
156 HYPERV_TSC_TIMECOUNT(mfence);
157
158 static void
159 hyperv_tsc_tcinit(void *dummy __unused)
160 {
161         uint64_t val, orig;
162
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 */
167                 return;
168
169         switch (cpu_vendor_id) {
170         case CPU_VENDOR_AMD:
171                 hyperv_tsc_timecounter.tc_get_timecount =
172                     hyperv_tsc_timecount_mfence;
173                 break;
174
175         case CPU_VENDOR_INTEL:
176                 hyperv_tsc_timecounter.tc_get_timecount =
177                     hyperv_tsc_timecount_lfence;
178                 break;
179
180         default:
181                 /* Unsupport CPU vendors. */
182                 return;
183         }
184
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");
190                 return;
191         }
192
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);
198
199         /* Register "enlightened" timecounter. */
200         tc_init(&hyperv_tsc_timecounter);
201
202         /* Add device for mmap(2). */
203         make_dev(&hyperv_tsc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0444,
204             HYPERV_REFTSC_DEVNAME);
205 }
206 SYSINIT(hyperv_tsc_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, hyperv_tsc_tcinit,
207     NULL);