hyperv: Implement cpucounters.
[dragonfly.git] / sys / dev / virtual / hyperv / hyperv.c
1 /*-
2  * Copyright (c) 2009-2012,2016 Microsoft Corp.
3  * Copyright (c) 2012 NetApp Inc.
4  * Copyright (c) 2012 Citrix Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/systimer.h>
32 #include <sys/systm.h>
33
34 #include <machine/cpufunc.h>
35
36 #include <dev/virtual/hyperv/hyperv_busdma.h>
37 #include <dev/virtual/hyperv/hyperv_machdep.h>
38 #include <dev/virtual/hyperv/hyperv_reg.h>
39 #include <dev/virtual/hyperv/hyperv_var.h>
40
41 #define HYPERV_DRAGONFLY_BUILD          0ULL
42 #define HYPERV_DRAGONFLY_VERSION        ((uint64_t)__DragonFly_version)
43 #define HYPERV_DRAGONFLY_OSID           0ULL
44
45 #define MSR_HV_GUESTID_BUILD_DRAGONFLY  \
46         (HYPERV_DRAGONFLY_BUILD & MSR_HV_GUESTID_BUILD_MASK)
47 #define MSR_HV_GUESTID_VERSION_DRAGONFLY \
48         ((HYPERV_DRAGONFLY_VERSION << MSR_HV_GUESTID_VERSION_SHIFT) & \
49          MSR_HV_GUESTID_VERSION_MASK)
50 #define MSR_HV_GUESTID_OSID_DRAGONFLY   \
51         ((HYPERV_DRAGONFLY_OSID << MSR_HV_GUESTID_OSID_SHIFT) & \
52          MSR_HV_GUESTID_OSID_MASK)
53
54 #define MSR_HV_GUESTID_DRAGONFLY        \
55         (MSR_HV_GUESTID_BUILD_DRAGONFLY | \
56          MSR_HV_GUESTID_VERSION_DRAGONFLY | \
57          MSR_HV_GUESTID_OSID_DRAGONFLY | \
58          MSR_HV_GUESTID_OSTYPE_FREEBSD)
59
60 struct hypercall_ctx {
61         void                    *hc_addr;
62         struct hyperv_dma       hc_dma;
63 };
64
65 static void             hyperv_cputimer_construct(struct cputimer *,
66                             sysclock_t);
67 static sysclock_t       hyperv_cputimer_count(void);
68 static boolean_t        hyperv_identify(void);
69 static int              hypercall_create(void);
70 static void             hypercall_destroy(void);
71 static void             hypercall_memfree(void);
72 static uint64_t         hyperv_tc64_rdmsr(void);
73
74 u_int                   hyperv_features;
75 static u_int            hyperv_recommends;
76
77 static u_int            hyperv_pm_features;
78 static u_int            hyperv_features3;
79
80 hyperv_tc64_t           hyperv_tc64;
81
82 static struct cputimer  hyperv_cputimer = {
83         .next           = SLIST_ENTRY_INITIALIZER,
84         .name           = "Hyper-V",
85         .pri            = CPUTIMER_PRI_VMM,
86         .type           = CPUTIMER_VMM,
87         .count          = hyperv_cputimer_count,
88         .fromhz         = cputimer_default_fromhz,
89         .fromus         = cputimer_default_fromus,
90         .construct      = hyperv_cputimer_construct,
91         .destruct       = cputimer_default_destruct,
92         .freq           = HYPERV_TIMER_FREQ
93 };
94
95 static struct cpucounter hyperv_cpucounter = {
96         .freq           = HYPERV_TIMER_FREQ,
97         .count          = hyperv_tc64_rdmsr,
98         .flags          = CPUCOUNTER_FLAG_MPSYNC,
99         .prio           = CPUCOUNTER_PRIO_VMM,
100         .type           = CPUCOUNTER_VMM
101 };
102
103 static struct hypercall_ctx     hypercall_context;
104
105 uint64_t
106 hypercall_post_message(bus_addr_t msg_paddr)
107 {
108         return hypercall_md(hypercall_context.hc_addr,
109             HYPERCALL_POST_MESSAGE, msg_paddr, 0);
110 }
111
112 static void
113 hyperv_cputimer_construct(struct cputimer *timer, sysclock_t oldclock)
114 {
115         timer->base = 0;
116         timer->base = oldclock - timer->count();
117 }
118
119 static sysclock_t
120 hyperv_cputimer_count(void)
121 {
122         uint64_t val;
123
124         val = rdmsr(MSR_HV_TIME_REF_COUNT);
125         return (val + hyperv_cputimer.base);
126 }
127
128 static void
129 hypercall_memfree(void)
130 {
131         hyperv_dmamem_free(&hypercall_context.hc_dma,
132             hypercall_context.hc_addr);
133         hypercall_context.hc_addr = NULL;
134 }
135
136 static int
137 hypercall_create(void)
138 {
139         uint64_t hc, hc_orig;
140
141         hypercall_context.hc_addr = hyperv_dmamem_alloc(NULL, PAGE_SIZE, 0,
142             PAGE_SIZE, &hypercall_context.hc_dma, BUS_DMA_WAITOK);
143         if (hypercall_context.hc_addr == NULL) {
144                 kprintf("hyperv: Hypercall page allocation failed\n");
145                 return ENOMEM;
146         }
147
148         /* Get the 'reserved' bits, which requires preservation. */
149         hc_orig = rdmsr(MSR_HV_HYPERCALL);
150
151         /*
152          * Setup the Hypercall page.
153          *
154          * NOTE: 'reserved' bits MUST be preserved.
155          */
156         hc = ((hypercall_context.hc_dma.hv_paddr >> PAGE_SHIFT) <<
157             MSR_HV_HYPERCALL_PGSHIFT) |
158             (hc_orig & MSR_HV_HYPERCALL_RSVD_MASK) |
159             MSR_HV_HYPERCALL_ENABLE;
160         wrmsr(MSR_HV_HYPERCALL, hc);
161
162         /*
163          * Confirm that Hypercall page did get setup.
164          */
165         hc = rdmsr(MSR_HV_HYPERCALL);
166         if ((hc & MSR_HV_HYPERCALL_ENABLE) == 0) {
167                 kprintf("hyperv: Hypercall setup failed\n");
168                 hypercall_memfree();
169                 return EIO;
170         }
171         if (bootverbose)
172                 kprintf("hyperv: Hypercall created\n");
173
174         return 0;
175 }
176
177 static void
178 hypercall_destroy(void)
179 {
180         uint64_t hc;
181
182         if (hypercall_context.hc_addr == NULL)
183                 return;
184
185         /* Disable Hypercall */
186         hc = rdmsr(MSR_HV_HYPERCALL);
187         wrmsr(MSR_HV_HYPERCALL, (hc & MSR_HV_HYPERCALL_RSVD_MASK));
188         hypercall_memfree();
189
190         if (bootverbose)
191                 kprintf("hyperv: Hypercall destroyed\n");
192 }
193
194 static uint64_t
195 hyperv_tc64_rdmsr(void)
196 {
197
198         return (rdmsr(MSR_HV_TIME_REF_COUNT));
199 }
200
201 static boolean_t
202 hyperv_identify(void)
203 {
204         u_int regs[4];
205         unsigned int maxleaf;
206
207         if (vmm_guest != VMM_GUEST_HYPERV)
208                 return (FALSE);
209
210         do_cpuid(CPUID_LEAF_HV_MAXLEAF, regs);
211         maxleaf = regs[0];
212         if (maxleaf < CPUID_LEAF_HV_LIMITS)
213                 return (FALSE);
214
215         do_cpuid(CPUID_LEAF_HV_INTERFACE, regs);
216         if (regs[0] != CPUID_HV_IFACE_HYPERV)
217                 return (FALSE);
218
219         do_cpuid(CPUID_LEAF_HV_FEATURES, regs);
220         if ((regs[0] & CPUID_HV_MSR_HYPERCALL) == 0) {
221                 /*
222                  * Hyper-V w/o Hypercall is impossible; someone
223                  * is faking Hyper-V.
224                  */
225                 return (FALSE);
226         }
227         hyperv_features = regs[0];
228         hyperv_pm_features = regs[2];
229         hyperv_features3 = regs[3];
230
231         do_cpuid(CPUID_LEAF_HV_IDENTITY, regs);
232         kprintf("Hyper-V Version: %d.%d.%d [SP%d]\n",
233             regs[1] >> 16, regs[1] & 0xffff, regs[0], regs[2]);
234
235         kprintf("  Features=0x%b\n", hyperv_features,
236             "\020"
237             "\001VPRUNTIME"     /* MSR_HV_VP_RUNTIME */
238             "\002TMREFCNT"      /* MSR_HV_TIME_REF_COUNT */
239             "\003SYNIC"         /* MSRs for SynIC */
240             "\004SYNTM"         /* MSRs for SynTimer */
241             "\005APIC"          /* MSR_HV_{EOI,ICR,TPR} */
242             "\006HYPERCALL"     /* MSR_HV_{GUEST_OS_ID,HYPERCALL} */
243             "\007VPINDEX"       /* MSR_HV_VP_INDEX */
244             "\010RESET"         /* MSR_HV_RESET */
245             "\011STATS"         /* MSR_HV_STATS_ */
246             "\012REFTSC"        /* MSR_HV_REFERENCE_TSC */
247             "\013IDLE"          /* MSR_HV_GUEST_IDLE */
248             "\014TMFREQ"        /* MSR_HV_{TSC,APIC}_FREQUENCY */
249             "\015DEBUG");       /* MSR_HV_SYNTH_DEBUG_ */
250         kprintf("  PM Features=0x%b [C%u]\n",
251             (hyperv_pm_features & ~CPUPM_HV_CSTATE_MASK),
252             "\020"
253             "\005C3HPET",       /* HPET is required for C3 state */
254             CPUPM_HV_CSTATE(hyperv_pm_features));
255         kprintf("  Features3=0x%b\n", hyperv_features3,
256             "\020"
257             "\001MWAIT"         /* MWAIT */
258             "\002DEBUG"         /* guest debug support */
259             "\003PERFMON"       /* performance monitor */
260             "\004PCPUDPE"       /* physical CPU dynamic partition event */
261             "\005XMMHC"         /* hypercall input through XMM regs */
262             "\006IDLE"          /* guest idle support */
263             "\007SLEEP"         /* hypervisor sleep support */
264             "\010NUMA"          /* NUMA distance query support */
265             "\011TMFREQ"        /* timer frequency query (TSC, LAPIC) */
266             "\012SYNCMC"        /* inject synthetic machine checks */
267             "\013CRASH"         /* MSRs for guest crash */
268             "\014DEBUGMSR"      /* MSRs for guest debug */
269             "\015NPIEP"         /* NPIEP */
270             "\016HVDIS");       /* disabling hypervisor */
271
272         do_cpuid(CPUID_LEAF_HV_RECOMMENDS, regs);
273         hyperv_recommends = regs[0];
274         if (bootverbose)
275                 kprintf("  Recommends: %08x %08x\n", regs[0], regs[1]);
276
277         do_cpuid(CPUID_LEAF_HV_LIMITS, regs);
278         if (bootverbose) {
279                 kprintf("  Limits: Vcpu:%d Lcpu:%d Int:%d\n",
280                     regs[0], regs[1], regs[2]);
281         }
282
283         if (maxleaf >= CPUID_LEAF_HV_HWFEATURES) {
284                 do_cpuid(CPUID_LEAF_HV_HWFEATURES, regs);
285                 if (bootverbose) {
286                         kprintf("  HW Features: %08x, AMD: %08x\n",
287                             regs[0], regs[3]);
288                 }
289         }
290
291         return (TRUE);
292 }
293
294 static void
295 hyperv_init(void *dummy __unused)
296 {
297         int error;
298
299         if (!hyperv_identify()) {
300                 /* Not Hyper-V; reset guest id to the generic one. */
301                 if (vmm_guest == VMM_GUEST_HYPERV)
302                         vmm_guest = VMM_GUEST_UNKNOWN;
303                 return;
304         }
305
306         /* Set guest id */
307         wrmsr(MSR_HV_GUEST_OS_ID, MSR_HV_GUESTID_DRAGONFLY);
308
309         if (hyperv_features & CPUID_HV_MSR_TIME_REFCNT) {
310                 /* Register Hyper-V cputimers */
311                 cputimer_register(&hyperv_cputimer);
312                 cputimer_select(&hyperv_cputimer, 0);
313                 cpucounter_register(&hyperv_cpucounter);
314                 hyperv_tc64 = hyperv_tc64_rdmsr;
315         }
316
317         error = hypercall_create();
318         if (error) {
319                 /* Can't perform any Hyper-V specific actions */
320                 vmm_guest = VMM_GUEST_UNKNOWN;
321         }
322
323         /* Machine dependent initialization. */
324         hyperv_md_init();
325 }
326 SYSINIT(hyperv_initialize, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST,
327     hyperv_init, NULL);
328
329 static void
330 hyperv_uninit(void *dummy __unused)
331 {
332         /* Machine dependent uninitialization. */
333         hyperv_md_uninit();
334
335         if (hyperv_features & CPUID_HV_MSR_TIME_REFCNT) {
336                 /* Deregister Hyper-V systimer */
337                 cputimer_deregister(&hyperv_cputimer);
338         }
339         hypercall_destroy();
340 }
341 SYSUNINIT(hyperv_uninitialize, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST,
342     hyperv_uninit, NULL);