/*- * Copyright (c) 2009-2012,2016 Microsoft Corp. * Copyright (c) 2012 NetApp Inc. * Copyright (c) 2012 Citrix Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #define HYPERV_DRAGONFLY_BUILD 0ULL #define HYPERV_DRAGONFLY_VERSION ((uint64_t)__DragonFly_version) #define HYPERV_DRAGONFLY_OSID 0ULL #define MSR_HV_GUESTID_BUILD_DRAGONFLY \ (HYPERV_DRAGONFLY_BUILD & MSR_HV_GUESTID_BUILD_MASK) #define MSR_HV_GUESTID_VERSION_DRAGONFLY \ ((HYPERV_DRAGONFLY_VERSION << MSR_HV_GUESTID_VERSION_SHIFT) & \ MSR_HV_GUESTID_VERSION_MASK) #define MSR_HV_GUESTID_OSID_DRAGONFLY \ ((HYPERV_DRAGONFLY_OSID << MSR_HV_GUESTID_OSID_SHIFT) & \ MSR_HV_GUESTID_OSID_MASK) #define MSR_HV_GUESTID_DRAGONFLY \ (MSR_HV_GUESTID_BUILD_DRAGONFLY | \ MSR_HV_GUESTID_VERSION_DRAGONFLY | \ MSR_HV_GUESTID_OSID_DRAGONFLY | \ MSR_HV_GUESTID_OSTYPE_FREEBSD) struct hypercall_ctx { void *hc_addr; struct hyperv_dma hc_dma; }; static void hyperv_cputimer_construct(struct cputimer *, sysclock_t); static sysclock_t hyperv_cputimer_count(void); static boolean_t hyperv_identify(void); static int hypercall_create(void); static void hypercall_destroy(void); static void hypercall_memfree(void); static uint64_t hyperv_tc64_rdmsr(void); u_int hyperv_features; static u_int hyperv_recommends; static u_int hyperv_pm_features; static u_int hyperv_features3; hyperv_tc64_t hyperv_tc64; static struct cputimer hyperv_cputimer = { .next = SLIST_ENTRY_INITIALIZER, .name = "Hyper-V", .pri = CPUTIMER_PRI_VMM, .type = CPUTIMER_VMM, .count = hyperv_cputimer_count, .fromhz = cputimer_default_fromhz, .fromus = cputimer_default_fromus, .construct = hyperv_cputimer_construct, .destruct = cputimer_default_destruct, .freq = HYPERV_TIMER_FREQ }; static struct cpucounter hyperv_cpucounter = { .freq = HYPERV_TIMER_FREQ, .count = hyperv_tc64_rdmsr, .flags = CPUCOUNTER_FLAG_MPSYNC, .prio = CPUCOUNTER_PRIO_VMM, .type = CPUCOUNTER_VMM }; static struct hypercall_ctx hypercall_context; uint64_t hypercall_post_message(bus_addr_t msg_paddr) { return hypercall_md(hypercall_context.hc_addr, HYPERCALL_POST_MESSAGE, msg_paddr, 0); } static void hyperv_cputimer_construct(struct cputimer *timer, sysclock_t oldclock) { timer->base = 0; timer->base = oldclock - timer->count(); } static sysclock_t hyperv_cputimer_count(void) { uint64_t val; val = rdmsr(MSR_HV_TIME_REF_COUNT); return (val + hyperv_cputimer.base); } static void hypercall_memfree(void) { hyperv_dmamem_free(&hypercall_context.hc_dma, hypercall_context.hc_addr); hypercall_context.hc_addr = NULL; } static int hypercall_create(void) { uint64_t hc, hc_orig; hypercall_context.hc_addr = hyperv_dmamem_alloc(NULL, PAGE_SIZE, 0, PAGE_SIZE, &hypercall_context.hc_dma, BUS_DMA_WAITOK); if (hypercall_context.hc_addr == NULL) { kprintf("hyperv: Hypercall page allocation failed\n"); return ENOMEM; } /* Get the 'reserved' bits, which requires preservation. */ hc_orig = rdmsr(MSR_HV_HYPERCALL); /* * Setup the Hypercall page. * * NOTE: 'reserved' bits MUST be preserved. */ hc = ((hypercall_context.hc_dma.hv_paddr >> PAGE_SHIFT) << MSR_HV_HYPERCALL_PGSHIFT) | (hc_orig & MSR_HV_HYPERCALL_RSVD_MASK) | MSR_HV_HYPERCALL_ENABLE; wrmsr(MSR_HV_HYPERCALL, hc); /* * Confirm that Hypercall page did get setup. */ hc = rdmsr(MSR_HV_HYPERCALL); if ((hc & MSR_HV_HYPERCALL_ENABLE) == 0) { kprintf("hyperv: Hypercall setup failed\n"); hypercall_memfree(); return EIO; } if (bootverbose) kprintf("hyperv: Hypercall created\n"); return 0; } static void hypercall_destroy(void) { uint64_t hc; if (hypercall_context.hc_addr == NULL) return; /* Disable Hypercall */ hc = rdmsr(MSR_HV_HYPERCALL); wrmsr(MSR_HV_HYPERCALL, (hc & MSR_HV_HYPERCALL_RSVD_MASK)); hypercall_memfree(); if (bootverbose) kprintf("hyperv: Hypercall destroyed\n"); } static uint64_t hyperv_tc64_rdmsr(void) { return (rdmsr(MSR_HV_TIME_REF_COUNT)); } static boolean_t hyperv_identify(void) { u_int regs[4]; unsigned int maxleaf; if (vmm_guest != VMM_GUEST_HYPERV) return (FALSE); do_cpuid(CPUID_LEAF_HV_MAXLEAF, regs); maxleaf = regs[0]; if (maxleaf < CPUID_LEAF_HV_LIMITS) return (FALSE); do_cpuid(CPUID_LEAF_HV_INTERFACE, regs); if (regs[0] != CPUID_HV_IFACE_HYPERV) return (FALSE); do_cpuid(CPUID_LEAF_HV_FEATURES, regs); if ((regs[0] & CPUID_HV_MSR_HYPERCALL) == 0) { /* * Hyper-V w/o Hypercall is impossible; someone * is faking Hyper-V. */ return (FALSE); } hyperv_features = regs[0]; hyperv_pm_features = regs[2]; hyperv_features3 = regs[3]; do_cpuid(CPUID_LEAF_HV_IDENTITY, regs); kprintf("Hyper-V Version: %d.%d.%d [SP%d]\n", regs[1] >> 16, regs[1] & 0xffff, regs[0], regs[2]); kprintf(" Features=0x%pb%i\n", "\020" "\001VPRUNTIME" /* MSR_HV_VP_RUNTIME */ "\002TMREFCNT" /* MSR_HV_TIME_REF_COUNT */ "\003SYNIC" /* MSRs for SynIC */ "\004SYNTM" /* MSRs for SynTimer */ "\005APIC" /* MSR_HV_{EOI,ICR,TPR} */ "\006HYPERCALL" /* MSR_HV_{GUEST_OS_ID,HYPERCALL} */ "\007VPINDEX" /* MSR_HV_VP_INDEX */ "\010RESET" /* MSR_HV_RESET */ "\011STATS" /* MSR_HV_STATS_ */ "\012REFTSC" /* MSR_HV_REFERENCE_TSC */ "\013IDLE" /* MSR_HV_GUEST_IDLE */ "\014TMFREQ" /* MSR_HV_{TSC,APIC}_FREQUENCY */ "\015DEBUG", /* MSR_HV_SYNTH_DEBUG_ */ hyperv_features); kprintf(" PM Features=0x%pb%i [C%u]\n", "\020" "\005C3HPET", /* HPET is required for C3 state */ (hyperv_pm_features & ~CPUPM_HV_CSTATE_MASK), CPUPM_HV_CSTATE(hyperv_pm_features)); kprintf(" Features3=0x%pb%i\n", "\020" "\001MWAIT" /* MWAIT */ "\002DEBUG" /* guest debug support */ "\003PERFMON" /* performance monitor */ "\004PCPUDPE" /* physical CPU dynamic partition event */ "\005XMMHC" /* hypercall input through XMM regs */ "\006IDLE" /* guest idle support */ "\007SLEEP" /* hypervisor sleep support */ "\010NUMA" /* NUMA distance query support */ "\011TMFREQ" /* timer frequency query (TSC, LAPIC) */ "\012SYNCMC" /* inject synthetic machine checks */ "\013CRASH" /* MSRs for guest crash */ "\014DEBUGMSR" /* MSRs for guest debug */ "\015NPIEP" /* NPIEP */ "\016HVDIS", /* disabling hypervisor */ hyperv_features3); do_cpuid(CPUID_LEAF_HV_RECOMMENDS, regs); hyperv_recommends = regs[0]; if (bootverbose) kprintf(" Recommends: %08x %08x\n", regs[0], regs[1]); do_cpuid(CPUID_LEAF_HV_LIMITS, regs); if (bootverbose) { kprintf(" Limits: Vcpu:%d Lcpu:%d Int:%d\n", regs[0], regs[1], regs[2]); } if (maxleaf >= CPUID_LEAF_HV_HWFEATURES) { do_cpuid(CPUID_LEAF_HV_HWFEATURES, regs); if (bootverbose) { kprintf(" HW Features: %08x, AMD: %08x\n", regs[0], regs[3]); } } return (TRUE); } static void hyperv_init(void *dummy __unused) { int error; if (!hyperv_identify()) { /* Not Hyper-V; reset guest id to the generic one. */ if (vmm_guest == VMM_GUEST_HYPERV) vmm_guest = VMM_GUEST_UNKNOWN; return; } /* Set guest id */ wrmsr(MSR_HV_GUEST_OS_ID, MSR_HV_GUESTID_DRAGONFLY); if (hyperv_features & CPUID_HV_MSR_TIME_REFCNT) { /* Register Hyper-V cputimers */ cputimer_register(&hyperv_cputimer); cputimer_select(&hyperv_cputimer, 0); cpucounter_register(&hyperv_cpucounter); hyperv_tc64 = hyperv_tc64_rdmsr; } error = hypercall_create(); if (error) { /* Can't perform any Hyper-V specific actions */ vmm_guest = VMM_GUEST_UNKNOWN; } /* Machine dependent initialization. */ hyperv_md_init(); } SYSINIT(hyperv_initialize, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST, hyperv_init, NULL); static void hyperv_uninit(void *dummy __unused) { /* Machine dependent uninitialization. */ hyperv_md_uninit(); if (hyperv_features & CPUID_HV_MSR_TIME_REFCNT) { /* Deregister Hyper-V systimer */ cputimer_deregister(&hyperv_cputimer); } hypercall_destroy(); } SYSUNINIT(hyperv_uninitialize, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST, hyperv_uninit, NULL);