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);
73 u_int hyperv_features
;
74 static u_int hyperv_recommends
;
76 static u_int hyperv_pm_features
;
77 static u_int hyperv_features3
;
79 static struct cputimer hyperv_cputimer
= {
80 SLIST_ENTRY_INITIALIZER
,
84 hyperv_cputimer_count
,
85 cputimer_default_fromhz
,
86 cputimer_default_fromus
,
87 hyperv_cputimer_construct
,
88 cputimer_default_destruct
,
93 static struct hypercall_ctx hypercall_context
;
96 hypercall_post_message(bus_addr_t msg_paddr
)
98 return hypercall_md(hypercall_context
.hc_addr
,
99 HYPERCALL_POST_MESSAGE
, msg_paddr
, 0);
103 hyperv_cputimer_construct(struct cputimer
*timer
, sysclock_t oldclock
)
106 timer
->base
= oldclock
- timer
->count();
110 hyperv_cputimer_count(void)
114 val
= rdmsr(MSR_HV_TIME_REF_COUNT
);
115 return (val
+ hyperv_cputimer
.base
);
119 hypercall_memfree(void)
121 hyperv_dmamem_free(&hypercall_context
.hc_dma
,
122 hypercall_context
.hc_addr
);
123 hypercall_context
.hc_addr
= NULL
;
127 hypercall_create(void)
129 uint64_t hc
, hc_orig
;
131 hypercall_context
.hc_addr
= hyperv_dmamem_alloc(NULL
, PAGE_SIZE
, 0,
132 PAGE_SIZE
, &hypercall_context
.hc_dma
, BUS_DMA_WAITOK
);
133 if (hypercall_context
.hc_addr
== NULL
) {
134 kprintf("hyperv: Hypercall page allocation failed\n");
138 /* Get the 'reserved' bits, which requires preservation. */
139 hc_orig
= rdmsr(MSR_HV_HYPERCALL
);
142 * Setup the Hypercall page.
144 * NOTE: 'reserved' bits MUST be preserved.
146 hc
= ((hypercall_context
.hc_dma
.hv_paddr
>> PAGE_SHIFT
) <<
147 MSR_HV_HYPERCALL_PGSHIFT
) |
148 (hc_orig
& MSR_HV_HYPERCALL_RSVD_MASK
) |
149 MSR_HV_HYPERCALL_ENABLE
;
150 wrmsr(MSR_HV_HYPERCALL
, hc
);
153 * Confirm that Hypercall page did get setup.
155 hc
= rdmsr(MSR_HV_HYPERCALL
);
156 if ((hc
& MSR_HV_HYPERCALL_ENABLE
) == 0) {
157 kprintf("hyperv: Hypercall setup failed\n");
162 kprintf("hyperv: Hypercall created\n");
168 hypercall_destroy(void)
172 if (hypercall_context
.hc_addr
== NULL
)
175 /* Disable Hypercall */
176 hc
= rdmsr(MSR_HV_HYPERCALL
);
177 wrmsr(MSR_HV_HYPERCALL
, (hc
& MSR_HV_HYPERCALL_RSVD_MASK
));
181 kprintf("hyperv: Hypercall destroyed\n");
185 hyperv_identify(void)
188 unsigned int maxleaf
;
190 if (vmm_guest
!= VMM_GUEST_HYPERV
)
193 do_cpuid(CPUID_LEAF_HV_MAXLEAF
, regs
);
195 if (maxleaf
< CPUID_LEAF_HV_LIMITS
)
198 do_cpuid(CPUID_LEAF_HV_INTERFACE
, regs
);
199 if (regs
[0] != CPUID_HV_IFACE_HYPERV
)
202 do_cpuid(CPUID_LEAF_HV_FEATURES
, regs
);
203 if ((regs
[0] & CPUID_HV_MSR_HYPERCALL
) == 0) {
205 * Hyper-V w/o Hypercall is impossible; someone
210 hyperv_features
= regs
[0];
211 hyperv_pm_features
= regs
[2];
212 hyperv_features3
= regs
[3];
214 do_cpuid(CPUID_LEAF_HV_IDENTITY
, regs
);
215 kprintf("Hyper-V Version: %d.%d.%d [SP%d]\n",
216 regs
[1] >> 16, regs
[1] & 0xffff, regs
[0], regs
[2]);
218 kprintf(" Features=0x%b\n", hyperv_features
,
220 "\001VPRUNTIME" /* MSR_HV_VP_RUNTIME */
221 "\002TMREFCNT" /* MSR_HV_TIME_REF_COUNT */
222 "\003SYNIC" /* MSRs for SynIC */
223 "\004SYNTM" /* MSRs for SynTimer */
224 "\005APIC" /* MSR_HV_{EOI,ICR,TPR} */
225 "\006HYPERCALL" /* MSR_HV_{GUEST_OS_ID,HYPERCALL} */
226 "\007VPINDEX" /* MSR_HV_VP_INDEX */
227 "\010RESET" /* MSR_HV_RESET */
228 "\011STATS" /* MSR_HV_STATS_ */
229 "\012REFTSC" /* MSR_HV_REFERENCE_TSC */
230 "\013IDLE" /* MSR_HV_GUEST_IDLE */
231 "\014TMFREQ" /* MSR_HV_{TSC,APIC}_FREQUENCY */
232 "\015DEBUG"); /* MSR_HV_SYNTH_DEBUG_ */
233 kprintf(" PM Features=0x%b [C%u]\n",
234 (hyperv_pm_features
& ~CPUPM_HV_CSTATE_MASK
),
236 "\005C3HPET", /* HPET is required for C3 state */
237 CPUPM_HV_CSTATE(hyperv_pm_features
));
238 kprintf(" Features3=0x%b\n", hyperv_features3
,
240 "\001MWAIT" /* MWAIT */
241 "\002DEBUG" /* guest debug support */
242 "\003PERFMON" /* performance monitor */
243 "\004PCPUDPE" /* physical CPU dynamic partition event */
244 "\005XMMHC" /* hypercall input through XMM regs */
245 "\006IDLE" /* guest idle support */
246 "\007SLEEP" /* hypervisor sleep support */
247 "\010NUMA" /* NUMA distance query support */
248 "\011TMFREQ" /* timer frequency query (TSC, LAPIC) */
249 "\012SYNCMC" /* inject synthetic machine checks */
250 "\013CRASH" /* MSRs for guest crash */
251 "\014DEBUGMSR" /* MSRs for guest debug */
252 "\015NPIEP" /* NPIEP */
253 "\016HVDIS"); /* disabling hypervisor */
255 do_cpuid(CPUID_LEAF_HV_RECOMMENDS
, regs
);
256 hyperv_recommends
= regs
[0];
258 kprintf(" Recommends: %08x %08x\n", regs
[0], regs
[1]);
260 do_cpuid(CPUID_LEAF_HV_LIMITS
, regs
);
262 kprintf(" Limits: Vcpu:%d Lcpu:%d Int:%d\n",
263 regs
[0], regs
[1], regs
[2]);
266 if (maxleaf
>= CPUID_LEAF_HV_HWFEATURES
) {
267 do_cpuid(CPUID_LEAF_HV_HWFEATURES
, regs
);
269 kprintf(" HW Features: %08x, AMD: %08x\n",
278 hyperv_init(void *dummy __unused
)
282 if (!hyperv_identify()) {
283 /* Not Hyper-V; reset guest id to the generic one. */
284 if (vmm_guest
== VMM_GUEST_HYPERV
)
285 vmm_guest
= VMM_GUEST_UNKNOWN
;
290 wrmsr(MSR_HV_GUEST_OS_ID
, MSR_HV_GUESTID_DRAGONFLY
);
292 if (hyperv_features
& CPUID_HV_MSR_TIME_REFCNT
) {
293 /* Register Hyper-V systimer */
294 cputimer_register(&hyperv_cputimer
);
295 cputimer_select(&hyperv_cputimer
, 0);
298 error
= hypercall_create();
300 /* Can't perform any Hyper-V specific actions */
301 vmm_guest
= VMM_GUEST_UNKNOWN
;
304 /* Machine dependent initialization. */
307 SYSINIT(hyperv_initialize
, SI_SUB_PRE_DRIVERS
, SI_ORDER_FIRST
,
311 hyperv_uninit(void *dummy __unused
)
313 /* Machine dependent uninitialization. */
316 if (hyperv_features
& CPUID_HV_MSR_TIME_REFCNT
) {
317 /* Deregister Hyper-V systimer */
318 cputimer_deregister(&hyperv_cputimer
);
322 SYSUNINIT(hyperv_uninitialize
, SI_SUB_PRE_DRIVERS
, SI_ORDER_FIRST
,
323 hyperv_uninit
, NULL
);