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.
30 * Implements low-level interactions with Hypver-V/Azure
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
39 #include <sys/timetc.h>
40 #include <machine/bus.h>
41 #include <machine/md_var.h>
43 #include <vm/vm_param.h>
47 #include "hv_vmbus_priv.h"
49 #define HV_NANOSECONDS_PER_SEC 1000000000L
51 #define HYPERV_INTERFACE 0x31237648 /* HV#1 */
53 static u_int hv_get_timecount(struct timecounter *tc);
55 u_int hyperv_features;
56 u_int hyperv_recommends;
58 static u_int hyperv_pm_features;
59 static u_int hyperv_features3;
64 hv_vmbus_context hv_vmbus_g_context = {
65 .syn_ic_initialized = FALSE,
66 .hypercall_page = NULL,
69 static struct timecounter hv_timecounter = {
70 hv_get_timecount, 0, ~0u, HV_NANOSECONDS_PER_SEC/100, "Hyper-V", HV_NANOSECONDS_PER_SEC/100
74 hv_get_timecount(struct timecounter *tc)
76 u_int now = rdmsr(HV_X64_MSR_TIME_REF_COUNT);
81 * @brief Invoke the specified hypercall
84 hv_vmbus_do_hypercall(uint64_t control, void* input, void* output)
87 uint64_t hv_status = 0;
88 uint64_t input_address = (input) ? hv_get_phys_addr(input) : 0;
89 uint64_t output_address = (output) ? hv_get_phys_addr(output) : 0;
90 volatile void* hypercall_page = hv_vmbus_g_context.hypercall_page;
92 __asm__ __volatile__ ("mov %0, %%r8" : : "r" (output_address): "r8");
93 __asm__ __volatile__ ("call *%3" : "=a"(hv_status):
94 "c" (control), "d" (input_address),
95 "m" (hypercall_page));
98 uint32_t control_high = control >> 32;
99 uint32_t control_low = control & 0xFFFFFFFF;
100 uint32_t hv_status_high = 1;
101 uint32_t hv_status_low = 1;
102 uint64_t input_address = (input) ? hv_get_phys_addr(input) : 0;
103 uint32_t input_address_high = input_address >> 32;
104 uint32_t input_address_low = input_address & 0xFFFFFFFF;
105 uint64_t output_address = (output) ? hv_get_phys_addr(output) : 0;
106 uint32_t output_address_high = output_address >> 32;
107 uint32_t output_address_low = output_address & 0xFFFFFFFF;
108 volatile void* hypercall_page = hv_vmbus_g_context.hypercall_page;
110 __asm__ __volatile__ ("call *%8" : "=d"(hv_status_high),
111 "=a"(hv_status_low) : "d" (control_high),
112 "a" (control_low), "b" (input_address_high),
113 "c" (input_address_low),
114 "D"(output_address_high),
115 "S"(output_address_low), "m" (hypercall_page));
116 return (hv_status_low | ((uint64_t)hv_status_high << 32));
117 #endif /* __x86_64__ */
121 * @brief Main initialization routine.
123 * This routine must be called
124 * before any other routines in here are called
129 hv_vmbus_x64_msr_hypercall_contents hypercall_msr;
130 void* virt_addr = NULL;
133 hv_vmbus_g_context.syn_ic_event_page,
135 sizeof(hv_vmbus_handle) * MAXCPU);
138 hv_vmbus_g_context.syn_ic_msg_page,
140 sizeof(hv_vmbus_handle) * MAXCPU);
142 if (vm_guest != VM_GUEST_HV)
148 uint64_t os_guest_info = HV_FREEBSD_GUEST_ID;
149 wrmsr(HV_X64_MSR_GUEST_OS_ID, os_guest_info);
150 hv_vmbus_g_context.guest_id = os_guest_info;
153 * See if the hypercall page is already set
155 hypercall_msr.as_uint64_t = rdmsr(HV_X64_MSR_HYPERCALL);
156 virt_addr = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
158 hypercall_msr.u.enable = 1;
159 hypercall_msr.u.guest_physical_address =
160 (hv_get_phys_addr(virt_addr) >> PAGE_SHIFT);
161 wrmsr(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64_t);
164 * Confirm that hypercall page did get set up
166 hypercall_msr.as_uint64_t = 0;
167 hypercall_msr.as_uint64_t = rdmsr(HV_X64_MSR_HYPERCALL);
169 if (!hypercall_msr.u.enable)
172 hv_vmbus_g_context.hypercall_page = virt_addr;
177 if (virt_addr != NULL) {
178 if (hypercall_msr.u.enable) {
179 hypercall_msr.as_uint64_t = 0;
180 wrmsr(HV_X64_MSR_HYPERCALL,
181 hypercall_msr.as_uint64_t);
184 free(virt_addr, M_DEVBUF);
190 * @brief Cleanup routine, called normally during driver unloading or exiting
193 hv_vmbus_cleanup(void)
195 hv_vmbus_x64_msr_hypercall_contents hypercall_msr;
197 if (hv_vmbus_g_context.guest_id == HV_FREEBSD_GUEST_ID) {
198 if (hv_vmbus_g_context.hypercall_page != NULL) {
199 hypercall_msr.as_uint64_t = 0;
200 wrmsr(HV_X64_MSR_HYPERCALL,
201 hypercall_msr.as_uint64_t);
202 free(hv_vmbus_g_context.hypercall_page, M_DEVBUF);
203 hv_vmbus_g_context.hypercall_page = NULL;
209 * @brief Post a message using the hypervisor message IPC.
210 * (This involves a hypercall.)
213 hv_vmbus_post_msg_via_msg_ipc(
214 hv_vmbus_connection_id connection_id,
215 hv_vmbus_msg_type message_type,
219 struct alignedinput {
221 hv_vmbus_input_post_message msg;
224 hv_vmbus_input_post_message* aligned_msg;
225 hv_vmbus_status status;
228 if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
231 addr = (size_t) malloc(sizeof(struct alignedinput), M_DEVBUF,
234 ("Error VMBUS: malloc failed to allocate message buffer!"));
238 aligned_msg = (hv_vmbus_input_post_message*)
239 (HV_ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
241 aligned_msg->connection_id = connection_id;
242 aligned_msg->message_type = message_type;
243 aligned_msg->payload_size = payload_size;
244 memcpy((void*) aligned_msg->payload, payload, payload_size);
246 status = hv_vmbus_do_hypercall(
247 HV_CALL_POST_MESSAGE, aligned_msg, 0) & 0xFFFF;
249 free((void *) addr, M_DEVBUF);
254 * @brief Signal an event on the specified connection using the hypervisor
255 * event IPC. (This involves a hypercall.)
258 hv_vmbus_signal_event(void *con_id)
260 hv_vmbus_status status;
262 status = hv_vmbus_do_hypercall(
263 HV_CALL_SIGNAL_EVENT,
271 * @brief hv_vmbus_synic_init
274 hv_vmbus_synic_init(void *arg)
278 uint64_t hv_vcpu_index;
279 hv_vmbus_synic_simp simp;
280 hv_vmbus_synic_siefp siefp;
281 hv_vmbus_synic_scontrol sctrl;
282 hv_vmbus_synic_sint shared_sint;
284 hv_setup_args* setup_args = (hv_setup_args *)arg;
286 cpu = PCPU_GET(cpuid);
288 if (hv_vmbus_g_context.hypercall_page == NULL)
292 * TODO: Check the version
294 version = rdmsr(HV_X64_MSR_SVERSION);
296 hv_vmbus_g_context.syn_ic_msg_page[cpu] =
297 setup_args->page_buffers[2 * cpu];
298 hv_vmbus_g_context.syn_ic_event_page[cpu] =
299 setup_args->page_buffers[2 * cpu + 1];
302 * Setup the Synic's message page
305 simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP);
306 simp.u.simp_enabled = 1;
307 simp.u.base_simp_gpa = ((hv_get_phys_addr(
308 hv_vmbus_g_context.syn_ic_msg_page[cpu])) >> PAGE_SHIFT);
310 wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t);
313 * Setup the Synic's event page
315 siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP);
316 siefp.u.siefp_enabled = 1;
317 siefp.u.base_siefp_gpa = ((hv_get_phys_addr(
318 hv_vmbus_g_context.syn_ic_event_page[cpu])) >> PAGE_SHIFT);
320 wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t);
322 /*HV_SHARED_SINT_IDT_VECTOR + 0x20; */
323 shared_sint.as_uint64_t = 0;
324 shared_sint.u.vector = setup_args->vector;
325 shared_sint.u.masked = FALSE;
326 shared_sint.u.auto_eoi = TRUE;
328 wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT,
329 shared_sint.as_uint64_t);
331 wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT,
332 shared_sint.as_uint64_t);
334 /* Enable the global synic bit */
335 sctrl.as_uint64_t = rdmsr(HV_X64_MSR_SCONTROL);
338 wrmsr(HV_X64_MSR_SCONTROL, sctrl.as_uint64_t);
340 hv_vmbus_g_context.syn_ic_initialized = TRUE;
343 * Set up the cpuid mapping from Hyper-V to FreeBSD.
344 * The array is indexed using FreeBSD cpuid.
346 hv_vcpu_index = rdmsr(HV_X64_MSR_VP_INDEX);
347 hv_vmbus_g_context.hv_vcpu_index[cpu] = (uint32_t)hv_vcpu_index;
353 * @brief Cleanup routine for hv_vmbus_synic_init()
355 void hv_vmbus_synic_cleanup(void *arg)
357 hv_vmbus_synic_sint shared_sint;
358 hv_vmbus_synic_simp simp;
359 hv_vmbus_synic_siefp siefp;
361 if (!hv_vmbus_g_context.syn_ic_initialized)
364 shared_sint.as_uint64_t = rdmsr(
365 HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT);
367 shared_sint.u.masked = 1;
370 * Disable the interrupt 0
373 HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT,
374 shared_sint.as_uint64_t);
376 shared_sint.as_uint64_t = rdmsr(
377 HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT);
379 shared_sint.u.masked = 1;
382 * Disable the interrupt 1
385 HV_X64_MSR_SINT0 + HV_VMBUS_TIMER_SINT,
386 shared_sint.as_uint64_t);
387 simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP);
388 simp.u.simp_enabled = 0;
389 simp.u.base_simp_gpa = 0;
391 wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t);
393 siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP);
394 siefp.u.siefp_enabled = 0;
395 siefp.u.base_siefp_gpa = 0;
397 wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t);
401 hyperv_identify(void)
404 unsigned int maxLeaf;
407 if (vm_guest != VM_GUEST_HV)
410 op = HV_CPU_ID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION;
413 if (maxLeaf < HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS)
416 op = HV_CPU_ID_FUNCTION_HV_INTERFACE;
418 if (regs[0] != HYPERV_INTERFACE)
421 op = HV_CPU_ID_FUNCTION_MS_HV_FEATURES;
423 if ((regs[0] & HV_FEATURE_MSR_HYPERCALL) == 0) {
425 * Hyper-V w/o Hypercall is impossible; someone
430 hyperv_features = regs[0];
431 hyperv_pm_features = regs[2];
432 hyperv_features3 = regs[3];
434 op = HV_CPU_ID_FUNCTION_MS_HV_VERSION;
436 printf("Hyper-V Version: %d.%d.%d [SP%d]\n",
437 regs[1] >> 16, regs[1] & 0xffff, regs[0], regs[2]);
439 printf(" Features=0x%b\n", hyperv_features,
441 "\001VPRUNTIME" /* MSR_VP_RUNTIME */
442 "\002TMREFCNT" /* MSR_TIME_REF_COUNT */
443 "\003SYNIC" /* MSRs for SynIC */
444 "\004SYNTM" /* MSRs for SynTimer */
445 "\005APIC" /* MSR_{EOI,ICR,TPR} */
446 "\006HYPERCALL" /* MSR_{GUEST_OS_ID,HYPERCALL} */
447 "\007VPINDEX" /* MSR_VP_INDEX */
448 "\010RESET" /* MSR_RESET */
449 "\011STATS" /* MSR_STATS_ */
450 "\012REFTSC" /* MSR_REFERENCE_TSC */
451 "\013IDLE" /* MSR_GUEST_IDLE */
452 "\014TMFREQ" /* MSR_{TSC,APIC}_FREQUENCY */
453 "\015DEBUG"); /* MSR_SYNTH_DEBUG_ */
454 printf(" PM Features=max C%u, 0x%b\n",
455 HV_PM_FEATURE_CSTATE(hyperv_pm_features),
456 (hyperv_pm_features & ~HV_PM_FEATURE_CSTATE_MASK),
458 "\005C3HPET"); /* HPET is required for C3 state */
459 printf(" Features3=0x%b\n", hyperv_features3,
461 "\001MWAIT" /* MWAIT */
462 "\002DEBUG" /* guest debug support */
463 "\003PERFMON" /* performance monitor */
464 "\004PCPUDPE" /* physical CPU dynamic partition event */
465 "\005XMMHC" /* hypercall input through XMM regs */
466 "\006IDLE" /* guest idle support */
467 "\007SLEEP" /* hypervisor sleep support */
468 "\010NUMA" /* NUMA distance query support */
469 "\011TMFREQ" /* timer frequency query (TSC, LAPIC) */
470 "\012SYNCMC" /* inject synthetic machine checks */
471 "\013CRASH" /* MSRs for guest crash */
472 "\014DEBUGMSR" /* MSRs for guest debug */
473 "\015NPIEP" /* NPIEP */
474 "\016HVDIS"); /* disabling hypervisor */
476 op = HV_CPU_ID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION;
478 hyperv_recommends = regs[0];
480 printf(" Recommends: %08x %08x\n", regs[0], regs[1]);
482 op = HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS;
485 printf(" Limits: Vcpu:%d Lcpu:%d Int:%d\n",
486 regs[0], regs[1], regs[2]);
489 if (maxLeaf >= HV_CPU_ID_FUNCTION_MS_HV_HARDWARE_FEATURE) {
490 op = HV_CPU_ID_FUNCTION_MS_HV_HARDWARE_FEATURE;
493 printf(" HW Features: %08x AMD: %08x\n",
502 hyperv_init(void *dummy __unused)
504 if (!hyperv_identify())
507 if (hyperv_features & HV_FEATURE_MSR_TIME_REFCNT) {
508 /* Register virtual timecount */
509 tc_init(&hv_timecounter);
512 SYSINIT(hyperv_initialize, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, hyperv_init,