2 * Copyright (c) 2009-2012,2016 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
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.
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.
29 #include <sys/param.h>
30 #include <sys/kernel.h>
31 #include <sys/systimer.h>
32 #include <sys/systm.h>
34 #include <machine/cpufunc.h>
36 #include <dev/virtual/hyperv/hyperv_busdma.h>
37 #include <dev/virtual/hyperv/vmbus/hyperv_machdep.h>
38 #include <dev/virtual/hyperv/vmbus/hyperv_reg.h>
39 #include <dev/virtual/hyperv/vmbus/hyperv_var.h>
41 #define HYPERV_DRAGONFLY_BUILD 0ULL
42 #define HYPERV_DRAGONFLY_VERSION ((uint64_t)__DragonFly_version)
43 #define HYPERV_DRAGONFLY_OSID 0ULL
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)
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)
60 struct hypercall_ctx
{
62 struct hyperv_dma hc_dma
;
65 static void hyperv_cputimer_construct(struct cputimer
*,
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);
74 u_int hyperv_features
;
75 static u_int hyperv_recommends
;
77 static u_int hyperv_pm_features
;
78 static u_int hyperv_features3
;
80 hyperv_tc64_t hyperv_tc64
;
82 static struct cputimer hyperv_cputimer
= {
83 SLIST_ENTRY_INITIALIZER
,
87 hyperv_cputimer_count
,
88 cputimer_default_fromhz
,
89 cputimer_default_fromus
,
90 hyperv_cputimer_construct
,
91 cputimer_default_destruct
,
96 static struct hypercall_ctx hypercall_context
;
99 hypercall_post_message(bus_addr_t msg_paddr
)
101 return hypercall_md(hypercall_context
.hc_addr
,
102 HYPERCALL_POST_MESSAGE
, msg_paddr
, 0);
106 hyperv_cputimer_construct(struct cputimer
*timer
, sysclock_t oldclock
)
109 timer
->base
= oldclock
- timer
->count();
113 hyperv_cputimer_count(void)
117 val
= rdmsr(MSR_HV_TIME_REF_COUNT
);
118 return (val
+ hyperv_cputimer
.base
);
122 hypercall_memfree(void)
124 hyperv_dmamem_free(&hypercall_context
.hc_dma
,
125 hypercall_context
.hc_addr
);
126 hypercall_context
.hc_addr
= NULL
;
130 hypercall_create(void)
132 uint64_t hc
, hc_orig
;
134 hypercall_context
.hc_addr
= hyperv_dmamem_alloc(NULL
, PAGE_SIZE
, 0,
135 PAGE_SIZE
, &hypercall_context
.hc_dma
, BUS_DMA_WAITOK
);
136 if (hypercall_context
.hc_addr
== NULL
) {
137 kprintf("hyperv: Hypercall page allocation failed\n");
141 /* Get the 'reserved' bits, which requires preservation. */
142 hc_orig
= rdmsr(MSR_HV_HYPERCALL
);
145 * Setup the Hypercall page.
147 * NOTE: 'reserved' bits MUST be preserved.
149 hc
= ((hypercall_context
.hc_dma
.hv_paddr
>> PAGE_SHIFT
) <<
150 MSR_HV_HYPERCALL_PGSHIFT
) |
151 (hc_orig
& MSR_HV_HYPERCALL_RSVD_MASK
) |
152 MSR_HV_HYPERCALL_ENABLE
;
153 wrmsr(MSR_HV_HYPERCALL
, hc
);
156 * Confirm that Hypercall page did get setup.
158 hc
= rdmsr(MSR_HV_HYPERCALL
);
159 if ((hc
& MSR_HV_HYPERCALL_ENABLE
) == 0) {
160 kprintf("hyperv: Hypercall setup failed\n");
165 kprintf("hyperv: Hypercall created\n");
171 hypercall_destroy(void)
175 if (hypercall_context
.hc_addr
== NULL
)
178 /* Disable Hypercall */
179 hc
= rdmsr(MSR_HV_HYPERCALL
);
180 wrmsr(MSR_HV_HYPERCALL
, (hc
& MSR_HV_HYPERCALL_RSVD_MASK
));
184 kprintf("hyperv: Hypercall destroyed\n");
188 hyperv_tc64_rdmsr(void)
191 return (rdmsr(MSR_HV_TIME_REF_COUNT
));
195 hyperv_identify(void)
198 unsigned int maxleaf
;
200 if (vmm_guest
!= VMM_GUEST_HYPERV
)
203 do_cpuid(CPUID_LEAF_HV_MAXLEAF
, regs
);
205 if (maxleaf
< CPUID_LEAF_HV_LIMITS
)
208 do_cpuid(CPUID_LEAF_HV_INTERFACE
, regs
);
209 if (regs
[0] != CPUID_HV_IFACE_HYPERV
)
212 do_cpuid(CPUID_LEAF_HV_FEATURES
, regs
);
213 if ((regs
[0] & CPUID_HV_MSR_HYPERCALL
) == 0) {
215 * Hyper-V w/o Hypercall is impossible; someone
220 hyperv_features
= regs
[0];
221 hyperv_pm_features
= regs
[2];
222 hyperv_features3
= regs
[3];
224 do_cpuid(CPUID_LEAF_HV_IDENTITY
, regs
);
225 kprintf("Hyper-V Version: %d.%d.%d [SP%d]\n",
226 regs
[1] >> 16, regs
[1] & 0xffff, regs
[0], regs
[2]);
228 kprintf(" Features=0x%b\n", hyperv_features
,
230 "\001VPRUNTIME" /* MSR_HV_VP_RUNTIME */
231 "\002TMREFCNT" /* MSR_HV_TIME_REF_COUNT */
232 "\003SYNIC" /* MSRs for SynIC */
233 "\004SYNTM" /* MSRs for SynTimer */
234 "\005APIC" /* MSR_HV_{EOI,ICR,TPR} */
235 "\006HYPERCALL" /* MSR_HV_{GUEST_OS_ID,HYPERCALL} */
236 "\007VPINDEX" /* MSR_HV_VP_INDEX */
237 "\010RESET" /* MSR_HV_RESET */
238 "\011STATS" /* MSR_HV_STATS_ */
239 "\012REFTSC" /* MSR_HV_REFERENCE_TSC */
240 "\013IDLE" /* MSR_HV_GUEST_IDLE */
241 "\014TMFREQ" /* MSR_HV_{TSC,APIC}_FREQUENCY */
242 "\015DEBUG"); /* MSR_HV_SYNTH_DEBUG_ */
243 kprintf(" PM Features=0x%b [C%u]\n",
244 (hyperv_pm_features
& ~CPUPM_HV_CSTATE_MASK
),
246 "\005C3HPET", /* HPET is required for C3 state */
247 CPUPM_HV_CSTATE(hyperv_pm_features
));
248 kprintf(" Features3=0x%b\n", hyperv_features3
,
250 "\001MWAIT" /* MWAIT */
251 "\002DEBUG" /* guest debug support */
252 "\003PERFMON" /* performance monitor */
253 "\004PCPUDPE" /* physical CPU dynamic partition event */
254 "\005XMMHC" /* hypercall input through XMM regs */
255 "\006IDLE" /* guest idle support */
256 "\007SLEEP" /* hypervisor sleep support */
257 "\010NUMA" /* NUMA distance query support */
258 "\011TMFREQ" /* timer frequency query (TSC, LAPIC) */
259 "\012SYNCMC" /* inject synthetic machine checks */
260 "\013CRASH" /* MSRs for guest crash */
261 "\014DEBUGMSR" /* MSRs for guest debug */
262 "\015NPIEP" /* NPIEP */
263 "\016HVDIS"); /* disabling hypervisor */
265 do_cpuid(CPUID_LEAF_HV_RECOMMENDS
, regs
);
266 hyperv_recommends
= regs
[0];
268 kprintf(" Recommends: %08x %08x\n", regs
[0], regs
[1]);
270 do_cpuid(CPUID_LEAF_HV_LIMITS
, regs
);
272 kprintf(" Limits: Vcpu:%d Lcpu:%d Int:%d\n",
273 regs
[0], regs
[1], regs
[2]);
276 if (maxleaf
>= CPUID_LEAF_HV_HWFEATURES
) {
277 do_cpuid(CPUID_LEAF_HV_HWFEATURES
, regs
);
279 kprintf(" HW Features: %08x, AMD: %08x\n",
288 hyperv_init(void *dummy __unused
)
292 if (!hyperv_identify()) {
293 /* Not Hyper-V; reset guest id to the generic one. */
294 if (vmm_guest
== VMM_GUEST_HYPERV
)
295 vmm_guest
= VMM_GUEST_UNKNOWN
;
300 wrmsr(MSR_HV_GUEST_OS_ID
, MSR_HV_GUESTID_DRAGONFLY
);
302 if (hyperv_features
& CPUID_HV_MSR_TIME_REFCNT
) {
303 /* Register Hyper-V systimer */
304 cputimer_register(&hyperv_cputimer
);
305 cputimer_select(&hyperv_cputimer
, 0);
306 hyperv_tc64
= hyperv_tc64_rdmsr
;
309 error
= hypercall_create();
311 /* Can't perform any Hyper-V specific actions */
312 vmm_guest
= VMM_GUEST_UNKNOWN
;
315 /* Machine dependent initialization. */
318 SYSINIT(hyperv_initialize
, SI_SUB_PRE_DRIVERS
, SI_ORDER_FIRST
,
322 hyperv_uninit(void *dummy __unused
)
324 /* Machine dependent uninitialization. */
327 if (hyperv_features
& CPUID_HV_MSR_TIME_REFCNT
) {
328 /* Deregister Hyper-V systimer */
329 cputimer_deregister(&hyperv_cputimer
);
333 SYSUNINIT(hyperv_uninitialize
, SI_SUB_PRE_DRIVERS
, SI_ORDER_FIRST
,
334 hyperv_uninit
, NULL
);