kernel: Remove last use cases of "%b" format.
[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%pb%i\n",
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             hyperv_features);
251         kprintf("  PM Features=0x%pb%i [C%u]\n",
252             "\020"
253             "\005C3HPET",       /* HPET is required for C3 state */
254             (hyperv_pm_features & ~CPUPM_HV_CSTATE_MASK),
255             CPUPM_HV_CSTATE(hyperv_pm_features));
256         kprintf("  Features3=0x%pb%i\n",
257             "\020"
258             "\001MWAIT"         /* MWAIT */
259             "\002DEBUG"         /* guest debug support */
260             "\003PERFMON"       /* performance monitor */
261             "\004PCPUDPE"       /* physical CPU dynamic partition event */
262             "\005XMMHC"         /* hypercall input through XMM regs */
263             "\006IDLE"          /* guest idle support */
264             "\007SLEEP"         /* hypervisor sleep support */
265             "\010NUMA"          /* NUMA distance query support */
266             "\011TMFREQ"        /* timer frequency query (TSC, LAPIC) */
267             "\012SYNCMC"        /* inject synthetic machine checks */
268             "\013CRASH"         /* MSRs for guest crash */
269             "\014DEBUGMSR"      /* MSRs for guest debug */
270             "\015NPIEP"         /* NPIEP */
271             "\016HVDIS",        /* disabling hypervisor */
272             hyperv_features3);
273
274         do_cpuid(CPUID_LEAF_HV_RECOMMENDS, regs);
275         hyperv_recommends = regs[0];
276         if (bootverbose)
277                 kprintf("  Recommends: %08x %08x\n", regs[0], regs[1]);
278
279         do_cpuid(CPUID_LEAF_HV_LIMITS, regs);
280         if (bootverbose) {
281                 kprintf("  Limits: Vcpu:%d Lcpu:%d Int:%d\n",
282                     regs[0], regs[1], regs[2]);
283         }
284
285         if (maxleaf >= CPUID_LEAF_HV_HWFEATURES) {
286                 do_cpuid(CPUID_LEAF_HV_HWFEATURES, regs);
287                 if (bootverbose) {
288                         kprintf("  HW Features: %08x, AMD: %08x\n",
289                             regs[0], regs[3]);
290                 }
291         }
292
293         return (TRUE);
294 }
295
296 static void
297 hyperv_init(void *dummy __unused)
298 {
299         int error;
300
301         if (!hyperv_identify()) {
302                 /* Not Hyper-V; reset guest id to the generic one. */
303                 if (vmm_guest == VMM_GUEST_HYPERV)
304                         vmm_guest = VMM_GUEST_UNKNOWN;
305                 return;
306         }
307
308         /* Set guest id */
309         wrmsr(MSR_HV_GUEST_OS_ID, MSR_HV_GUESTID_DRAGONFLY);
310
311         if (hyperv_features & CPUID_HV_MSR_TIME_REFCNT) {
312                 /* Register Hyper-V cputimers */
313                 cputimer_register(&hyperv_cputimer);
314                 cputimer_select(&hyperv_cputimer, 0);
315                 cpucounter_register(&hyperv_cpucounter);
316                 hyperv_tc64 = hyperv_tc64_rdmsr;
317         }
318
319         error = hypercall_create();
320         if (error) {
321                 /* Can't perform any Hyper-V specific actions */
322                 vmm_guest = VMM_GUEST_UNKNOWN;
323         }
324
325         /* Machine dependent initialization. */
326         hyperv_md_init();
327 }
328 SYSINIT(hyperv_initialize, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST,
329     hyperv_init, NULL);
330
331 static void
332 hyperv_uninit(void *dummy __unused)
333 {
334         /* Machine dependent uninitialization. */
335         hyperv_md_uninit();
336
337         if (hyperv_features & CPUID_HV_MSR_TIME_REFCNT) {
338                 /* Deregister Hyper-V systimer */
339                 cputimer_deregister(&hyperv_cputimer);
340         }
341         hypercall_destroy();
342 }
343 SYSUNINIT(hyperv_uninitialize, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST,
344     hyperv_uninit, NULL);