1250199Sgrehan/*- 2250199Sgrehan * Copyright (c) 2009-2012 Microsoft Corp. 3250199Sgrehan * Copyright (c) 2012 NetApp Inc. 4250199Sgrehan * Copyright (c) 2012 Citrix Inc. 5250199Sgrehan * All rights reserved. 6250199Sgrehan * 7250199Sgrehan * Redistribution and use in source and binary forms, with or without 8250199Sgrehan * modification, are permitted provided that the following conditions 9250199Sgrehan * are met: 10250199Sgrehan * 1. Redistributions of source code must retain the above copyright 11250199Sgrehan * notice unmodified, this list of conditions, and the following 12250199Sgrehan * disclaimer. 13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 14250199Sgrehan * notice, this list of conditions and the following disclaimer in the 15250199Sgrehan * documentation and/or other materials provided with the distribution. 16250199Sgrehan * 17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27250199Sgrehan */ 28250199Sgrehan 29250199Sgrehan/** 30250199Sgrehan * Implements low-level interactions with Hypver-V/Azure 31250199Sgrehan */ 32256071Sgibbs#include <sys/cdefs.h> 33256071Sgibbs__FBSDID("$FreeBSD: releng/10.3/sys/dev/hyperv/vmbus/hv_hv.c 303984 2016-08-12 04:01:16Z glebius $"); 34250199Sgrehan 35250199Sgrehan#include <sys/param.h> 36303984Sglebius#include <sys/kernel.h> 37250199Sgrehan#include <sys/malloc.h> 38250199Sgrehan#include <sys/pcpu.h> 39250199Sgrehan#include <sys/timetc.h> 40250199Sgrehan#include <machine/bus.h> 41295789Ssephe#include <machine/md_var.h> 42250199Sgrehan#include <vm/vm.h> 43250199Sgrehan#include <vm/vm_param.h> 44250199Sgrehan#include <vm/pmap.h> 45250199Sgrehan 46250199Sgrehan 47250199Sgrehan#include "hv_vmbus_priv.h" 48250199Sgrehan 49250199Sgrehan#define HV_NANOSECONDS_PER_SEC 1000000000L 50250199Sgrehan 51250199Sgrehan 52250199Sgrehanstatic u_int hv_get_timecount(struct timecounter *tc); 53250199Sgrehan 54303984Sglebiusu_int hyperv_features; 55303984Sglebiusu_int hyperv_recommends; 56303984Sglebius 57250199Sgrehan/** 58250199Sgrehan * Globals 59250199Sgrehan */ 60250199Sgrehanhv_vmbus_context hv_vmbus_g_context = { 61250199Sgrehan .syn_ic_initialized = FALSE, 62250199Sgrehan .hypercall_page = NULL, 63250199Sgrehan}; 64250199Sgrehan 65250199Sgrehanstatic struct timecounter hv_timecounter = { 66250199Sgrehan hv_get_timecount, 0, ~0u, HV_NANOSECONDS_PER_SEC/100, "Hyper-V", HV_NANOSECONDS_PER_SEC/100 67250199Sgrehan}; 68250199Sgrehan 69250199Sgrehanstatic u_int 70250199Sgrehanhv_get_timecount(struct timecounter *tc) 71250199Sgrehan{ 72255414Sgrehan u_int now = rdmsr(HV_X64_MSR_TIME_REF_COUNT); 73250199Sgrehan return (now); 74250199Sgrehan} 75250199Sgrehan 76250199Sgrehan/** 77250199Sgrehan * @brief Query the cpuid for presence of windows hypervisor 78250199Sgrehan */ 79250199Sgrehanint 80250199Sgrehanhv_vmbus_query_hypervisor_presence(void) 81250199Sgrehan{ 82295789Ssephe if (vm_guest != VM_GUEST_HV) 83256071Sgibbs return (0); 84256071Sgibbs 85295789Ssephe return (hv_high >= HV_X64_CPUID_MIN && hv_high <= HV_X64_CPUID_MAX); 86250199Sgrehan} 87250199Sgrehan 88250199Sgrehan/** 89250199Sgrehan * @brief Get version of the windows hypervisor 90250199Sgrehan */ 91250199Sgrehanstatic int 92250199Sgrehanhv_vmbus_get_hypervisor_version(void) 93250199Sgrehan{ 94295789Ssephe u_int regs[4]; 95250199Sgrehan unsigned int maxLeaf; 96250199Sgrehan unsigned int op; 97250199Sgrehan 98250199Sgrehan /* 99250199Sgrehan * Its assumed that this is called after confirming that 100250199Sgrehan * Viridian is present 101250199Sgrehan * Query id and revision. 102250199Sgrehan */ 103250199Sgrehan op = HV_CPU_ID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION; 104295789Ssephe do_cpuid(op, regs); 105250199Sgrehan 106295789Ssephe maxLeaf = regs[0]; 107250199Sgrehan op = HV_CPU_ID_FUNCTION_HV_INTERFACE; 108295789Ssephe do_cpuid(op, regs); 109250199Sgrehan 110250199Sgrehan if (maxLeaf >= HV_CPU_ID_FUNCTION_MS_HV_VERSION) { 111250199Sgrehan op = HV_CPU_ID_FUNCTION_MS_HV_VERSION; 112295789Ssephe do_cpuid(op, regs); 113250199Sgrehan } 114250199Sgrehan return (maxLeaf); 115250199Sgrehan} 116250199Sgrehan 117250199Sgrehan/** 118250199Sgrehan * @brief Invoke the specified hypercall 119250199Sgrehan */ 120250199Sgrehanstatic uint64_t 121250199Sgrehanhv_vmbus_do_hypercall(uint64_t control, void* input, void* output) 122250199Sgrehan{ 123250199Sgrehan#ifdef __x86_64__ 124250199Sgrehan uint64_t hv_status = 0; 125250199Sgrehan uint64_t input_address = (input) ? hv_get_phys_addr(input) : 0; 126250199Sgrehan uint64_t output_address = (output) ? hv_get_phys_addr(output) : 0; 127250199Sgrehan volatile void* hypercall_page = hv_vmbus_g_context.hypercall_page; 128250199Sgrehan 129250199Sgrehan __asm__ __volatile__ ("mov %0, %%r8" : : "r" (output_address): "r8"); 130250199Sgrehan __asm__ __volatile__ ("call *%3" : "=a"(hv_status): 131250199Sgrehan "c" (control), "d" (input_address), 132250199Sgrehan "m" (hypercall_page)); 133250199Sgrehan return (hv_status); 134250199Sgrehan#else 135250199Sgrehan uint32_t control_high = control >> 32; 136250199Sgrehan uint32_t control_low = control & 0xFFFFFFFF; 137250199Sgrehan uint32_t hv_status_high = 1; 138250199Sgrehan uint32_t hv_status_low = 1; 139250199Sgrehan uint64_t input_address = (input) ? hv_get_phys_addr(input) : 0; 140250199Sgrehan uint32_t input_address_high = input_address >> 32; 141250199Sgrehan uint32_t input_address_low = input_address & 0xFFFFFFFF; 142250199Sgrehan uint64_t output_address = (output) ? hv_get_phys_addr(output) : 0; 143250199Sgrehan uint32_t output_address_high = output_address >> 32; 144250199Sgrehan uint32_t output_address_low = output_address & 0xFFFFFFFF; 145250199Sgrehan volatile void* hypercall_page = hv_vmbus_g_context.hypercall_page; 146250199Sgrehan 147250199Sgrehan __asm__ __volatile__ ("call *%8" : "=d"(hv_status_high), 148250199Sgrehan "=a"(hv_status_low) : "d" (control_high), 149250199Sgrehan "a" (control_low), "b" (input_address_high), 150250199Sgrehan "c" (input_address_low), 151250199Sgrehan "D"(output_address_high), 152250199Sgrehan "S"(output_address_low), "m" (hypercall_page)); 153250199Sgrehan return (hv_status_low | ((uint64_t)hv_status_high << 32)); 154250199Sgrehan#endif /* __x86_64__ */ 155250199Sgrehan} 156250199Sgrehan 157250199Sgrehan/** 158250199Sgrehan * @brief Main initialization routine. 159250199Sgrehan * 160250199Sgrehan * This routine must be called 161250199Sgrehan * before any other routines in here are called 162250199Sgrehan */ 163250199Sgrehanint 164250199Sgrehanhv_vmbus_init(void) 165250199Sgrehan{ 166250199Sgrehan int max_leaf; 167250199Sgrehan hv_vmbus_x64_msr_hypercall_contents hypercall_msr; 168250199Sgrehan void* virt_addr = 0; 169250199Sgrehan 170250199Sgrehan memset( 171250199Sgrehan hv_vmbus_g_context.syn_ic_event_page, 172250199Sgrehan 0, 173250199Sgrehan sizeof(hv_vmbus_handle) * MAXCPU); 174250199Sgrehan 175250199Sgrehan memset( 176250199Sgrehan hv_vmbus_g_context.syn_ic_msg_page, 177250199Sgrehan 0, 178250199Sgrehan sizeof(hv_vmbus_handle) * MAXCPU); 179250199Sgrehan 180256758Sgibbs if (vm_guest != VM_GUEST_HV) 181250199Sgrehan goto cleanup; 182250199Sgrehan 183250199Sgrehan max_leaf = hv_vmbus_get_hypervisor_version(); 184250199Sgrehan 185250199Sgrehan /* 186250199Sgrehan * Write our OS info 187250199Sgrehan */ 188250199Sgrehan uint64_t os_guest_info = HV_FREEBSD_GUEST_ID; 189255414Sgrehan wrmsr(HV_X64_MSR_GUEST_OS_ID, os_guest_info); 190250199Sgrehan hv_vmbus_g_context.guest_id = os_guest_info; 191250199Sgrehan 192250199Sgrehan /* 193250199Sgrehan * See if the hypercall page is already set 194250199Sgrehan */ 195255414Sgrehan hypercall_msr.as_uint64_t = rdmsr(HV_X64_MSR_HYPERCALL); 196250199Sgrehan virt_addr = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO); 197250199Sgrehan KASSERT(virt_addr != NULL, 198250199Sgrehan ("Error VMBUS: malloc failed to allocate page during init!")); 199250199Sgrehan if (virt_addr == NULL) 200250199Sgrehan goto cleanup; 201250199Sgrehan 202256276Sdim hypercall_msr.u.enable = 1; 203256276Sdim hypercall_msr.u.guest_physical_address = 204250199Sgrehan (hv_get_phys_addr(virt_addr) >> PAGE_SHIFT); 205255414Sgrehan wrmsr(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64_t); 206250199Sgrehan 207250199Sgrehan /* 208250199Sgrehan * Confirm that hypercall page did get set up 209250199Sgrehan */ 210250199Sgrehan hypercall_msr.as_uint64_t = 0; 211255414Sgrehan hypercall_msr.as_uint64_t = rdmsr(HV_X64_MSR_HYPERCALL); 212250199Sgrehan 213256276Sdim if (!hypercall_msr.u.enable) 214250199Sgrehan goto cleanup; 215250199Sgrehan 216250199Sgrehan hv_vmbus_g_context.hypercall_page = virt_addr; 217250199Sgrehan 218295789Ssephe hv_et_init(); 219250199Sgrehan 220250199Sgrehan return (0); 221250199Sgrehan 222250199Sgrehan cleanup: 223250199Sgrehan if (virt_addr != NULL) { 224256276Sdim if (hypercall_msr.u.enable) { 225250199Sgrehan hypercall_msr.as_uint64_t = 0; 226255414Sgrehan wrmsr(HV_X64_MSR_HYPERCALL, 227250199Sgrehan hypercall_msr.as_uint64_t); 228250199Sgrehan } 229250199Sgrehan 230250199Sgrehan free(virt_addr, M_DEVBUF); 231250199Sgrehan } 232250199Sgrehan return (ENOTSUP); 233250199Sgrehan} 234250199Sgrehan 235250199Sgrehan/** 236250199Sgrehan * @brief Cleanup routine, called normally during driver unloading or exiting 237250199Sgrehan */ 238250199Sgrehanvoid 239250199Sgrehanhv_vmbus_cleanup(void) 240250199Sgrehan{ 241250199Sgrehan hv_vmbus_x64_msr_hypercall_contents hypercall_msr; 242250199Sgrehan 243250199Sgrehan if (hv_vmbus_g_context.guest_id == HV_FREEBSD_GUEST_ID) { 244250199Sgrehan if (hv_vmbus_g_context.hypercall_page != NULL) { 245250199Sgrehan hypercall_msr.as_uint64_t = 0; 246255414Sgrehan wrmsr(HV_X64_MSR_HYPERCALL, 247250199Sgrehan hypercall_msr.as_uint64_t); 248250199Sgrehan free(hv_vmbus_g_context.hypercall_page, M_DEVBUF); 249250199Sgrehan hv_vmbus_g_context.hypercall_page = NULL; 250250199Sgrehan } 251250199Sgrehan } 252250199Sgrehan} 253250199Sgrehan 254250199Sgrehan/** 255250199Sgrehan * @brief Post a message using the hypervisor message IPC. 256250199Sgrehan * (This involves a hypercall.) 257250199Sgrehan */ 258250199Sgrehanhv_vmbus_status 259250199Sgrehanhv_vmbus_post_msg_via_msg_ipc( 260250199Sgrehan hv_vmbus_connection_id connection_id, 261250199Sgrehan hv_vmbus_msg_type message_type, 262250199Sgrehan void* payload, 263250199Sgrehan size_t payload_size) 264250199Sgrehan{ 265250199Sgrehan struct alignedinput { 266250199Sgrehan uint64_t alignment8; 267250199Sgrehan hv_vmbus_input_post_message msg; 268250199Sgrehan }; 269250199Sgrehan 270250199Sgrehan hv_vmbus_input_post_message* aligned_msg; 271250199Sgrehan hv_vmbus_status status; 272250199Sgrehan size_t addr; 273250199Sgrehan 274250199Sgrehan if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) 275250199Sgrehan return (EMSGSIZE); 276250199Sgrehan 277250199Sgrehan addr = (size_t) malloc(sizeof(struct alignedinput), M_DEVBUF, 278250199Sgrehan M_ZERO | M_NOWAIT); 279250199Sgrehan KASSERT(addr != 0, 280250199Sgrehan ("Error VMBUS: malloc failed to allocate message buffer!")); 281250199Sgrehan if (addr == 0) 282250199Sgrehan return (ENOMEM); 283250199Sgrehan 284250199Sgrehan aligned_msg = (hv_vmbus_input_post_message*) 285250199Sgrehan (HV_ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN)); 286250199Sgrehan 287250199Sgrehan aligned_msg->connection_id = connection_id; 288250199Sgrehan aligned_msg->message_type = message_type; 289250199Sgrehan aligned_msg->payload_size = payload_size; 290250199Sgrehan memcpy((void*) aligned_msg->payload, payload, payload_size); 291250199Sgrehan 292250199Sgrehan status = hv_vmbus_do_hypercall( 293250199Sgrehan HV_CALL_POST_MESSAGE, aligned_msg, 0) & 0xFFFF; 294250199Sgrehan 295250199Sgrehan free((void *) addr, M_DEVBUF); 296250199Sgrehan return (status); 297250199Sgrehan} 298250199Sgrehan 299250199Sgrehan/** 300250199Sgrehan * @brief Signal an event on the specified connection using the hypervisor 301250199Sgrehan * event IPC. (This involves a hypercall.) 302250199Sgrehan */ 303250199Sgrehanhv_vmbus_status 304283280Swhuhv_vmbus_signal_event(void *con_id) 305250199Sgrehan{ 306250199Sgrehan hv_vmbus_status status; 307250199Sgrehan 308250199Sgrehan status = hv_vmbus_do_hypercall( 309250199Sgrehan HV_CALL_SIGNAL_EVENT, 310283280Swhu con_id, 311250199Sgrehan 0) & 0xFFFF; 312250199Sgrehan 313250199Sgrehan return (status); 314250199Sgrehan} 315250199Sgrehan 316250199Sgrehan/** 317250199Sgrehan * @brief hv_vmbus_synic_init 318250199Sgrehan */ 319250199Sgrehanvoid 320255414Sgrehanhv_vmbus_synic_init(void *arg) 321250199Sgrehan 322250199Sgrehan{ 323250199Sgrehan int cpu; 324283280Swhu uint64_t hv_vcpu_index; 325250199Sgrehan hv_vmbus_synic_simp simp; 326250199Sgrehan hv_vmbus_synic_siefp siefp; 327250199Sgrehan hv_vmbus_synic_scontrol sctrl; 328250199Sgrehan hv_vmbus_synic_sint shared_sint; 329250199Sgrehan uint64_t version; 330255414Sgrehan hv_setup_args* setup_args = (hv_setup_args *)arg; 331250199Sgrehan 332250199Sgrehan cpu = PCPU_GET(cpuid); 333250199Sgrehan 334250199Sgrehan if (hv_vmbus_g_context.hypercall_page == NULL) 335250199Sgrehan return; 336250199Sgrehan 337250199Sgrehan /* 338250199Sgrehan * TODO: Check the version 339250199Sgrehan */ 340255414Sgrehan version = rdmsr(HV_X64_MSR_SVERSION); 341255414Sgrehan 342283280Swhu hv_vmbus_g_context.syn_ic_msg_page[cpu] = 343283280Swhu setup_args->page_buffers[2 * cpu]; 344283280Swhu hv_vmbus_g_context.syn_ic_event_page[cpu] = 345283280Swhu setup_args->page_buffers[2 * cpu + 1]; 346250199Sgrehan 347250199Sgrehan /* 348250199Sgrehan * Setup the Synic's message page 349250199Sgrehan */ 350250199Sgrehan 351255414Sgrehan simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); 352256276Sdim simp.u.simp_enabled = 1; 353256276Sdim simp.u.base_simp_gpa = ((hv_get_phys_addr( 354250199Sgrehan hv_vmbus_g_context.syn_ic_msg_page[cpu])) >> PAGE_SHIFT); 355250199Sgrehan 356255414Sgrehan wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t); 357250199Sgrehan 358250199Sgrehan /* 359250199Sgrehan * Setup the Synic's event page 360250199Sgrehan */ 361255414Sgrehan siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP); 362256276Sdim siefp.u.siefp_enabled = 1; 363256276Sdim siefp.u.base_siefp_gpa = ((hv_get_phys_addr( 364250199Sgrehan hv_vmbus_g_context.syn_ic_event_page[cpu])) >> PAGE_SHIFT); 365250199Sgrehan 366255414Sgrehan wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); 367250199Sgrehan 368255414Sgrehan /*HV_SHARED_SINT_IDT_VECTOR + 0x20; */ 369283280Swhu shared_sint.as_uint64_t = 0; 370256276Sdim shared_sint.u.vector = setup_args->vector; 371256276Sdim shared_sint.u.masked = FALSE; 372283280Swhu shared_sint.u.auto_eoi = TRUE; 373250199Sgrehan 374255414Sgrehan wrmsr(HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, 375250199Sgrehan shared_sint.as_uint64_t); 376250199Sgrehan 377250199Sgrehan /* Enable the global synic bit */ 378255414Sgrehan sctrl.as_uint64_t = rdmsr(HV_X64_MSR_SCONTROL); 379256276Sdim sctrl.u.enable = 1; 380250199Sgrehan 381255414Sgrehan wrmsr(HV_X64_MSR_SCONTROL, sctrl.as_uint64_t); 382250199Sgrehan 383250199Sgrehan hv_vmbus_g_context.syn_ic_initialized = TRUE; 384250199Sgrehan 385283280Swhu /* 386283280Swhu * Set up the cpuid mapping from Hyper-V to FreeBSD. 387283280Swhu * The array is indexed using FreeBSD cpuid. 388283280Swhu */ 389283280Swhu hv_vcpu_index = rdmsr(HV_X64_MSR_VP_INDEX); 390283280Swhu hv_vmbus_g_context.hv_vcpu_index[cpu] = (uint32_t)hv_vcpu_index; 391283280Swhu 392250199Sgrehan return; 393250199Sgrehan} 394250199Sgrehan 395250199Sgrehan/** 396250199Sgrehan * @brief Cleanup routine for hv_vmbus_synic_init() 397250199Sgrehan */ 398250199Sgrehanvoid hv_vmbus_synic_cleanup(void *arg) 399250199Sgrehan{ 400250199Sgrehan hv_vmbus_synic_sint shared_sint; 401250199Sgrehan hv_vmbus_synic_simp simp; 402250199Sgrehan hv_vmbus_synic_siefp siefp; 403250199Sgrehan 404250199Sgrehan if (!hv_vmbus_g_context.syn_ic_initialized) 405250199Sgrehan return; 406250199Sgrehan 407255414Sgrehan shared_sint.as_uint64_t = rdmsr( 408250199Sgrehan HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT); 409250199Sgrehan 410256276Sdim shared_sint.u.masked = 1; 411250199Sgrehan 412250199Sgrehan /* 413250199Sgrehan * Disable the interrupt 414250199Sgrehan */ 415255414Sgrehan wrmsr( 416250199Sgrehan HV_X64_MSR_SINT0 + HV_VMBUS_MESSAGE_SINT, 417250199Sgrehan shared_sint.as_uint64_t); 418250199Sgrehan 419255414Sgrehan simp.as_uint64_t = rdmsr(HV_X64_MSR_SIMP); 420256276Sdim simp.u.simp_enabled = 0; 421256276Sdim simp.u.base_simp_gpa = 0; 422250199Sgrehan 423255414Sgrehan wrmsr(HV_X64_MSR_SIMP, simp.as_uint64_t); 424250199Sgrehan 425255414Sgrehan siefp.as_uint64_t = rdmsr(HV_X64_MSR_SIEFP); 426256276Sdim siefp.u.siefp_enabled = 0; 427256276Sdim siefp.u.base_siefp_gpa = 0; 428250199Sgrehan 429255414Sgrehan wrmsr(HV_X64_MSR_SIEFP, siefp.as_uint64_t); 430250199Sgrehan} 431250199Sgrehan 432303984Sglebiusstatic bool 433303984Sglebiushyperv_identify(void) 434303984Sglebius{ 435303984Sglebius u_int regs[4]; 436303984Sglebius unsigned int maxLeaf; 437303984Sglebius unsigned int op; 438303984Sglebius 439303984Sglebius if (vm_guest != VM_GUEST_HV) 440303984Sglebius return (false); 441303984Sglebius 442303984Sglebius op = HV_CPU_ID_FUNCTION_HV_VENDOR_AND_MAX_FUNCTION; 443303984Sglebius do_cpuid(op, regs); 444303984Sglebius maxLeaf = regs[0]; 445303984Sglebius if (maxLeaf < HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS) 446303984Sglebius return (false); 447303984Sglebius 448303984Sglebius op = HV_CPU_ID_FUNCTION_HV_INTERFACE; 449303984Sglebius do_cpuid(op, regs); 450303984Sglebius if (regs[0] != 0x31237648 /* HV#1 */) 451303984Sglebius return (false); 452303984Sglebius 453303984Sglebius op = HV_CPU_ID_FUNCTION_MS_HV_FEATURES; 454303984Sglebius do_cpuid(op, regs); 455303984Sglebius if ((regs[0] & HV_FEATURE_MSR_HYPERCALL) == 0) { 456303984Sglebius /* 457303984Sglebius * Hyper-V w/o Hypercall is impossible; someone 458303984Sglebius * is faking Hyper-V. 459303984Sglebius */ 460303984Sglebius return (false); 461303984Sglebius } 462303984Sglebius hyperv_features = regs[0]; 463303984Sglebius 464303984Sglebius op = HV_CPU_ID_FUNCTION_MS_HV_VERSION; 465303984Sglebius do_cpuid(op, regs); 466303984Sglebius printf("Hyper-V Version: %d.%d.%d [SP%d]\n", 467303984Sglebius regs[1] >> 16, regs[1] & 0xffff, regs[0], regs[2]); 468303984Sglebius 469303984Sglebius printf(" Features: 0x%b\n", hyperv_features, 470303984Sglebius "\020" 471303984Sglebius "\001VPRUNTIME" 472303984Sglebius "\002TMREFCNT" 473303984Sglebius "\003SYNCIC" 474303984Sglebius "\004SYNCTM" 475303984Sglebius "\005APIC" 476303984Sglebius "\006HYERCALL" 477303984Sglebius "\007VPINDEX" 478303984Sglebius "\010RESET" 479303984Sglebius "\011STATS" 480303984Sglebius "\012REFTSC" 481303984Sglebius "\013IDLE" 482303984Sglebius "\014TMFREQ" 483303984Sglebius "\015DEBUG"); 484303984Sglebius 485303984Sglebius op = HV_CPU_ID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION; 486303984Sglebius do_cpuid(op, regs); 487303984Sglebius hyperv_recommends = regs[0]; 488303984Sglebius if (bootverbose) 489303984Sglebius printf(" Recommends: %08x %08x\n", regs[0], regs[1]); 490303984Sglebius 491303984Sglebius op = HV_CPU_ID_FUNCTION_MS_HV_IMPLEMENTATION_LIMITS; 492303984Sglebius do_cpuid(op, regs); 493303984Sglebius if (bootverbose) { 494303984Sglebius printf(" Limits: Vcpu:%d Lcpu:%d Int:%d\n", 495303984Sglebius regs[0], regs[1], regs[2]); 496303984Sglebius } 497303984Sglebius 498303984Sglebius if (maxLeaf >= HV_CPU_ID_FUNCTION_MS_HV_HARDWARE_FEATURE) { 499303984Sglebius op = HV_CPU_ID_FUNCTION_MS_HV_HARDWARE_FEATURE; 500303984Sglebius do_cpuid(op, regs); 501303984Sglebius if (bootverbose) { 502303984Sglebius printf(" HW Features: %08x AMD: %08x\n", 503303984Sglebius regs[0], regs[3]); 504303984Sglebius } 505303984Sglebius } 506303984Sglebius 507303984Sglebius return (true); 508303984Sglebius} 509303984Sglebius 510303984Sglebiusstatic void 511303984Sglebiushyperv_init(void *dummy __unused) 512303984Sglebius{ 513303984Sglebius if (!hyperv_identify()) 514303984Sglebius return; 515303984Sglebius 516303984Sglebius if (hyperv_features & HV_FEATURE_MSR_TIME_REFCNT) { 517303984Sglebius /* Register virtual timecount */ 518303984Sglebius tc_init(&hv_timecounter); 519303984Sglebius } 520303984Sglebius} 521303984SglebiusSYSINIT(hyperv_initialize, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, hyperv_init, NULL); 522