Date: Wed, 1 Jun 2016 09:20:52 +0000 (UTC) From: Sepherosa Ziehau <sephe@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r301113 - in head/sys: conf dev/hyperv/vmbus dev/hyperv/vmbus/amd64 dev/hyperv/vmbus/i386 modules/hyperv/vmbus Message-ID: <201606010920.u519Kqh6068360@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: sephe Date: Wed Jun 1 09:20:52 2016 New Revision: 301113 URL: https://svnweb.freebsd.org/changeset/base/301113 Log: hyperv: Rename some cleaned up/almost cleaned up files MFC after: 1 week Sponsored by: Microsoft OSTC Added: head/sys/dev/hyperv/vmbus/amd64/vmbus_vector.S - copied unchanged from r301112, head/sys/dev/hyperv/vmbus/amd64/hv_vector.S head/sys/dev/hyperv/vmbus/hyperv.c - copied unchanged from r301112, head/sys/dev/hyperv/vmbus/hv_hv.c head/sys/dev/hyperv/vmbus/i386/vmbus_vector.S - copied unchanged from r301112, head/sys/dev/hyperv/vmbus/i386/hv_vector.S head/sys/dev/hyperv/vmbus/vmbus.c - copied unchanged from r301112, head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c head/sys/dev/hyperv/vmbus/vmbus_et.c - copied unchanged from r301112, head/sys/dev/hyperv/vmbus/hv_et.c Deleted: head/sys/dev/hyperv/vmbus/amd64/hv_vector.S head/sys/dev/hyperv/vmbus/hv_et.c head/sys/dev/hyperv/vmbus/hv_hv.c head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c head/sys/dev/hyperv/vmbus/i386/hv_vector.S Modified: head/sys/conf/files.amd64 head/sys/conf/files.i386 head/sys/modules/hyperv/vmbus/Makefile Modified: head/sys/conf/files.amd64 ============================================================================== --- head/sys/conf/files.amd64 Wed Jun 1 08:20:10 2016 (r301112) +++ head/sys/conf/files.amd64 Wed Jun 1 09:20:52 2016 (r301113) @@ -273,12 +273,12 @@ dev/hyperv/utilities/hv_util.c option dev/hyperv/vmbus/hv_channel.c optional hyperv dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv dev/hyperv/vmbus/hv_connection.c optional hyperv -dev/hyperv/vmbus/hv_hv.c optional hyperv -dev/hyperv/vmbus/hv_et.c optional hyperv dev/hyperv/vmbus/hv_ring_buffer.c optional hyperv -dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c optional hyperv +dev/hyperv/vmbus/hyperv.c optional hyperv dev/hyperv/vmbus/hyperv_busdma.c optional hyperv -dev/hyperv/vmbus/amd64/hv_vector.S optional hyperv +dev/hyperv/vmbus/vmbus.c optional hyperv +dev/hyperv/vmbus/vmbus_et.c optional hyperv +dev/hyperv/vmbus/amd64/vmbus_vector.S optional hyperv dev/nfe/if_nfe.c optional nfe pci dev/ntb/if_ntb/if_ntb.c optional if_ntb dev/ntb/ntb_hw/ntb_hw.c optional if_ntb | ntb_hw Modified: head/sys/conf/files.i386 ============================================================================== --- head/sys/conf/files.i386 Wed Jun 1 08:20:10 2016 (r301112) +++ head/sys/conf/files.i386 Wed Jun 1 09:20:52 2016 (r301113) @@ -248,12 +248,12 @@ dev/hyperv/utilities/hv_util.c option dev/hyperv/vmbus/hv_channel.c optional hyperv dev/hyperv/vmbus/hv_channel_mgmt.c optional hyperv dev/hyperv/vmbus/hv_connection.c optional hyperv -dev/hyperv/vmbus/hv_hv.c optional hyperv -dev/hyperv/vmbus/hv_et.c optional hyperv dev/hyperv/vmbus/hv_ring_buffer.c optional hyperv -dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c optional hyperv +dev/hyperv/vmbus/hyperv.c optional hyperv dev/hyperv/vmbus/hyperv_busdma.c optional hyperv -dev/hyperv/vmbus/i386/hv_vector.S optional hyperv +dev/hyperv/vmbus/vmbus.c optional hyperv +dev/hyperv/vmbus/vmbus_et.c optional hyperv +dev/hyperv/vmbus/i386/vmbus_vector.S optional hyperv dev/ichwd/ichwd.c optional ichwd dev/if_ndis/if_ndis.c optional ndis dev/if_ndis/if_ndis_pccard.c optional ndis pccard Copied: head/sys/dev/hyperv/vmbus/amd64/vmbus_vector.S (from r301112, head/sys/dev/hyperv/vmbus/amd64/hv_vector.S) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/hyperv/vmbus/amd64/vmbus_vector.S Wed Jun 1 09:20:52 2016 (r301113, copy of r301112, head/sys/dev/hyperv/vmbus/amd64/hv_vector.S) @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2016 Microsoft Corp. + * 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. + * + * $FreeBSD$ + */ + +#include <machine/asmacros.h> +#include <machine/specialreg.h> + +#include "assym.s" + +/* + * This is the Hyper-V vmbus channel direct callback interrupt. + * Only used when it is running on Hyper-V. + */ + .text + SUPERALIGN_TEXT +IDTVEC(vmbus_isr) + PUSH_FRAME + FAKE_MCOUNT(TF_RIP(%rsp)) + movq %rsp, %rdi + call vmbus_handle_intr + MEXITCOUNT + jmp doreti Copied: head/sys/dev/hyperv/vmbus/hyperv.c (from r301112, head/sys/dev/hyperv/vmbus/hv_hv.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/hyperv/vmbus/hyperv.c Wed Jun 1 09:20:52 2016 (r301113, copy of r301112, head/sys/dev/hyperv/vmbus/hv_hv.c) @@ -0,0 +1,404 @@ +/*- + * 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. + */ + +/** + * Implements low-level interactions with Hypver-V/Azure + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/pcpu.h> +#include <sys/timetc.h> +#include <machine/bus.h> +#include <machine/md_var.h> +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/pmap.h> + +#include <dev/hyperv/include/hyperv_busdma.h> +#include <dev/hyperv/vmbus/hv_vmbus_priv.h> +#include <dev/hyperv/vmbus/hyperv_reg.h> +#include <dev/hyperv/vmbus/hyperv_var.h> +#include <dev/hyperv/vmbus/vmbus_var.h> + +#define HYPERV_FREEBSD_BUILD 0ULL +#define HYPERV_FREEBSD_VERSION ((uint64_t)__FreeBSD_version) +#define HYPERV_FREEBSD_OSID 0ULL + +#define MSR_HV_GUESTID_BUILD_FREEBSD \ + (HYPERV_FREEBSD_BUILD & MSR_HV_GUESTID_BUILD_MASK) +#define MSR_HV_GUESTID_VERSION_FREEBSD \ + ((HYPERV_FREEBSD_VERSION << MSR_HV_GUESTID_VERSION_SHIFT) & \ + MSR_HV_GUESTID_VERSION_MASK) +#define MSR_HV_GUESTID_OSID_FREEBSD \ + ((HYPERV_FREEBSD_OSID << MSR_HV_GUESTID_OSID_SHIFT) & \ + MSR_HV_GUESTID_OSID_MASK) + +#define MSR_HV_GUESTID_FREEBSD \ + (MSR_HV_GUESTID_BUILD_FREEBSD | \ + MSR_HV_GUESTID_VERSION_FREEBSD | \ + MSR_HV_GUESTID_OSID_FREEBSD | \ + MSR_HV_GUESTID_OSTYPE_FREEBSD) + +struct hypercall_ctx { + void *hc_addr; + struct hyperv_dma hc_dma; +}; + +static u_int hyperv_get_timecount(struct timecounter *tc); + +u_int hyperv_features; +u_int hyperv_recommends; + +static u_int hyperv_pm_features; +static u_int hyperv_features3; + +static struct timecounter hyperv_timecounter = { + .tc_get_timecount = hyperv_get_timecount, + .tc_poll_pps = NULL, + .tc_counter_mask = 0xffffffff, + .tc_frequency = HYPERV_TIMER_FREQ, + .tc_name = "Hyper-V", + .tc_quality = 2000, + .tc_flags = 0, + .tc_priv = NULL +}; + +static struct hypercall_ctx hypercall_context; + +static u_int +hyperv_get_timecount(struct timecounter *tc __unused) +{ + return rdmsr(MSR_HV_TIME_REF_COUNT); +} + +/** + * @brief Invoke the specified hypercall + */ +static uint64_t +hv_vmbus_do_hypercall(uint64_t control, void* input, void* output) +{ +#ifdef __x86_64__ + uint64_t hv_status = 0; + uint64_t input_address = (input) ? hv_get_phys_addr(input) : 0; + uint64_t output_address = (output) ? hv_get_phys_addr(output) : 0; + volatile void *hypercall_page = hypercall_context.hc_addr; + + __asm__ __volatile__ ("mov %0, %%r8" : : "r" (output_address): "r8"); + __asm__ __volatile__ ("call *%3" : "=a"(hv_status): + "c" (control), "d" (input_address), + "m" (hypercall_page)); + return (hv_status); +#else + uint32_t control_high = control >> 32; + uint32_t control_low = control & 0xFFFFFFFF; + uint32_t hv_status_high = 1; + uint32_t hv_status_low = 1; + uint64_t input_address = (input) ? hv_get_phys_addr(input) : 0; + uint32_t input_address_high = input_address >> 32; + uint32_t input_address_low = input_address & 0xFFFFFFFF; + uint64_t output_address = (output) ? hv_get_phys_addr(output) : 0; + uint32_t output_address_high = output_address >> 32; + uint32_t output_address_low = output_address & 0xFFFFFFFF; + volatile void *hypercall_page = hypercall_context.hc_addr; + + __asm__ __volatile__ ("call *%8" : "=d"(hv_status_high), + "=a"(hv_status_low) : "d" (control_high), + "a" (control_low), "b" (input_address_high), + "c" (input_address_low), + "D"(output_address_high), + "S"(output_address_low), "m" (hypercall_page)); + return (hv_status_low | ((uint64_t)hv_status_high << 32)); +#endif /* __x86_64__ */ +} + +/** + * @brief Post a message using the hypervisor message IPC. + * (This involves a hypercall.) + */ +hv_vmbus_status +hv_vmbus_post_msg_via_msg_ipc( + hv_vmbus_connection_id connection_id, + hv_vmbus_msg_type message_type, + void* payload, + size_t payload_size) +{ + struct alignedinput { + uint64_t alignment8; + hv_vmbus_input_post_message msg; + }; + + hv_vmbus_input_post_message* aligned_msg; + hv_vmbus_status status; + size_t addr; + + if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) + return (EMSGSIZE); + + addr = (size_t) malloc(sizeof(struct alignedinput), M_DEVBUF, + M_ZERO | M_NOWAIT); + KASSERT(addr != 0, + ("Error VMBUS: malloc failed to allocate message buffer!")); + if (addr == 0) + return (ENOMEM); + + aligned_msg = (hv_vmbus_input_post_message*) + (HV_ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN)); + + aligned_msg->connection_id = connection_id; + aligned_msg->message_type = message_type; + aligned_msg->payload_size = payload_size; + memcpy((void*) aligned_msg->payload, payload, payload_size); + + status = hv_vmbus_do_hypercall( + HV_CALL_POST_MESSAGE, aligned_msg, 0) & 0xFFFF; + + free((void *) addr, M_DEVBUF); + return (status); +} + +/** + * @brief Signal an event on the specified connection using the hypervisor + * event IPC. (This involves a hypercall.) + */ +hv_vmbus_status +hv_vmbus_signal_event(void *con_id) +{ + hv_vmbus_status status; + + status = hv_vmbus_do_hypercall( + HV_CALL_SIGNAL_EVENT, + con_id, + 0) & 0xFFFF; + + return (status); +} + +int +hyperv_guid2str(const struct hv_guid *guid, char *buf, size_t sz) +{ + const uint8_t *d = guid->data; + + return snprintf(buf, sz, "%02x%02x%02x%02x-" + "%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x", + d[3], d[2], d[1], d[0], + d[5], d[4], d[7], d[6], d[8], d[9], + d[10], d[11], d[12], d[13], d[14], d[15]); +} + +static bool +hyperv_identify(void) +{ + u_int regs[4]; + unsigned int maxleaf; + + if (vm_guest != VM_GUEST_HV) + 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); + printf("Hyper-V Version: %d.%d.%d [SP%d]\n", + regs[1] >> 16, regs[1] & 0xffff, regs[0], regs[2]); + + printf(" Features=0x%b\n", hyperv_features, + "\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_ */ + printf(" PM Features=0x%b [C%u]\n", + (hyperv_pm_features & ~CPUPM_HV_CSTATE_MASK), + "\020" + "\005C3HPET", /* HPET is required for C3 state */ + CPUPM_HV_CSTATE(hyperv_pm_features)); + printf(" Features3=0x%b\n", hyperv_features3, + "\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 */ + + do_cpuid(CPUID_LEAF_HV_RECOMMENDS, regs); + hyperv_recommends = regs[0]; + if (bootverbose) + printf(" Recommends: %08x %08x\n", regs[0], regs[1]); + + do_cpuid(CPUID_LEAF_HV_LIMITS, regs); + if (bootverbose) { + printf(" 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) { + printf(" HW Features: %08x, AMD: %08x\n", + regs[0], regs[3]); + } + } + + return (true); +} + +static void +hyperv_init(void *dummy __unused) +{ + if (!hyperv_identify()) { + /* Not Hyper-V; reset guest id to the generic one. */ + if (vm_guest == VM_GUEST_HV) + vm_guest = VM_GUEST_VM; + return; + } + + /* Set guest id */ + wrmsr(MSR_HV_GUEST_OS_ID, MSR_HV_GUESTID_FREEBSD); + + if (hyperv_features & CPUID_HV_MSR_TIME_REFCNT) { + /* Register Hyper-V timecounter */ + tc_init(&hyperv_timecounter); + } +} +SYSINIT(hyperv_initialize, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, hyperv_init, + NULL); + +static void +hypercall_memfree(void) +{ + hyperv_dmamem_free(&hypercall_context.hc_dma, + hypercall_context.hc_addr); + hypercall_context.hc_addr = NULL; +} + +static void +hypercall_create(void *arg __unused) +{ + uint64_t hc, hc_orig; + + if (vm_guest != VM_GUEST_HV) + return; + + 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) { + printf("hyperv: Hypercall page allocation failed\n"); + /* Can't perform any Hyper-V specific actions */ + vm_guest = VM_GUEST_VM; + return; + } + + /* 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) { + printf("hyperv: Hypercall setup failed\n"); + hypercall_memfree(); + /* Can't perform any Hyper-V specific actions */ + vm_guest = VM_GUEST_VM; + return; + } + if (bootverbose) + printf("hyperv: Hypercall created\n"); +} +SYSINIT(hypercall_ctor, SI_SUB_DRIVERS, SI_ORDER_FIRST, hypercall_create, NULL); + +static void +hypercall_destroy(void *arg __unused) +{ + 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) + printf("hyperv: Hypercall destroyed\n"); +} +SYSUNINIT(hypercall_dtor, SI_SUB_DRIVERS, SI_ORDER_FIRST, hypercall_destroy, + NULL); Copied: head/sys/dev/hyperv/vmbus/i386/vmbus_vector.S (from r301112, head/sys/dev/hyperv/vmbus/i386/hv_vector.S) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/hyperv/vmbus/i386/vmbus_vector.S Wed Jun 1 09:20:52 2016 (r301113, copy of r301112, head/sys/dev/hyperv/vmbus/i386/hv_vector.S) @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2016 Microsoft Corp. + * 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. + * + * $FreeBSD$ + */ + +#include <machine/asmacros.h> +#include <machine/specialreg.h> + +#include "assym.s" + +/* + * This is the Hyper-V vmbus channel direct callback interrupt. + * Only used when it is running on Hyper-V. + */ + .text + SUPERALIGN_TEXT +IDTVEC(vmbus_isr) + PUSH_FRAME + SET_KERNEL_SREGS + cld + FAKE_MCOUNT(TF_EIP(%esp)) + pushl %esp + call vmbus_handle_intr + add $4, %esp + MEXITCOUNT + jmp doreti Copied: head/sys/dev/hyperv/vmbus/vmbus.c (from r301112, head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/hyperv/vmbus/vmbus.c Wed Jun 1 09:20:52 2016 (r301113, copy of r301112, head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c) @@ -0,0 +1,759 @@ +/*- + * 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. + */ + +/* + * VM Bus Driver Implementation + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/proc.h> +#include <sys/sysctl.h> +#include <sys/syslog.h> +#include <sys/systm.h> +#include <sys/rtprio.h> +#include <sys/interrupt.h> +#include <sys/sx.h> +#include <sys/taskqueue.h> +#include <sys/mutex.h> +#include <sys/smp.h> + +#include <machine/resource.h> +#include <sys/rman.h> + +#include <machine/stdarg.h> +#include <machine/intr_machdep.h> +#include <machine/md_var.h> +#include <machine/segments.h> +#include <sys/pcpu.h> +#include <x86/apicvar.h> + +#include <dev/hyperv/include/hyperv.h> +#include <dev/hyperv/vmbus/hv_vmbus_priv.h> +#include <dev/hyperv/vmbus/hyperv_reg.h> +#include <dev/hyperv/vmbus/hyperv_var.h> +#include <dev/hyperv/vmbus/vmbus_reg.h> +#include <dev/hyperv/vmbus/vmbus_var.h> + +#include <contrib/dev/acpica/include/acpi.h> +#include "acpi_if.h" + +struct vmbus_softc *vmbus_sc; + +extern inthand_t IDTVEC(vmbus_isr); + +static void +vmbus_msg_task(void *xsc, int pending __unused) +{ + struct vmbus_softc *sc = xsc; + volatile struct vmbus_message *msg; + + msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE; + for (;;) { + const hv_vmbus_channel_msg_table_entry *entry; + hv_vmbus_channel_msg_header *hdr; + hv_vmbus_channel_msg_type msg_type; + + if (msg->msg_type == VMBUS_MSGTYPE_NONE) + break; /* no message */ + + /* XXX: update messageHandler interface */ + hdr = __DEVOLATILE(hv_vmbus_channel_msg_header *, + msg->msg_data); + msg_type = hdr->message_type; + + if (msg_type >= HV_CHANNEL_MESSAGE_COUNT) { + printf("VMBUS: unknown message type = %d\n", msg_type); + goto handled; + } + + entry = &g_channel_message_table[msg_type]; + if (entry->messageHandler) + entry->messageHandler(hdr); +handled: + msg->msg_type = VMBUS_MSGTYPE_NONE; + /* + * Make sure the write to msg_type (i.e. set to + * VMBUS_MSGTYPE_NONE) happens before we read the + * msg_flags and EOMing. Otherwise, the EOMing will + * not deliver any more messages since there is no + * empty slot + * + * NOTE: + * mb() is used here, since atomic_thread_fence_seq_cst() + * will become compiler fence on UP kernel. + */ + mb(); + if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) { + /* + * This will cause message queue rescan to possibly + * deliver another msg from the hypervisor + */ + wrmsr(MSR_HV_EOM, 0); + } + } +} + +static __inline int +vmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu) +{ + volatile struct vmbus_message *msg; + struct vmbus_message *msg_base; + + msg_base = VMBUS_PCPU_GET(sc, message, cpu); + + /* + * Check event timer. + * + * TODO: move this to independent IDT vector. + */ + msg = msg_base + VMBUS_SINT_TIMER; + if (msg->msg_type == VMBUS_MSGTYPE_TIMER_EXPIRED) { + msg->msg_type = VMBUS_MSGTYPE_NONE; + + vmbus_et_intr(frame); + + /* + * Make sure the write to msg_type (i.e. set to + * VMBUS_MSGTYPE_NONE) happens before we read the + * msg_flags and EOMing. Otherwise, the EOMing will + * not deliver any more messages since there is no + * empty slot + * + * NOTE: + * mb() is used here, since atomic_thread_fence_seq_cst() + * will become compiler fence on UP kernel. + */ + mb(); + if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) { + /* + * This will cause message queue rescan to possibly + * deliver another msg from the hypervisor + */ + wrmsr(MSR_HV_EOM, 0); + } + } + + /* + * Check events. Hot path for network and storage I/O data; high rate. + * + * NOTE: + * As recommended by the Windows guest fellows, we check events before + * checking messages. + */ + sc->vmbus_event_proc(sc, cpu); + + /* + * Check messages. Mainly management stuffs; ultra low rate. + */ + msg = msg_base + VMBUS_SINT_MESSAGE; + if (__predict_false(msg->msg_type != VMBUS_MSGTYPE_NONE)) { + taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu), + VMBUS_PCPU_PTR(sc, message_task, cpu)); + } + + return (FILTER_HANDLED); +} + +void +vmbus_handle_intr(struct trapframe *trap_frame) +{ + struct vmbus_softc *sc = vmbus_get_softc(); + int cpu = curcpu; + + /* + * Disable preemption. + */ + critical_enter(); + + /* + * Do a little interrupt counting. + */ + (*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++; + + vmbus_handle_intr1(sc, trap_frame, cpu); + + /* + * Enable preemption. + */ + critical_exit(); +} + +static void +vmbus_synic_setup(void *xsc) +{ + struct vmbus_softc *sc = xsc; + int cpu = curcpu; + uint64_t val, orig; + uint32_t sint; + + if (hyperv_features & CPUID_HV_MSR_VP_INDEX) { + /* + * Save virtual processor id. + */ + VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(MSR_HV_VP_INDEX); + } else { + /* + * XXX + * Virtual processoor id is only used by a pretty broken + * channel selection code from storvsc. It's nothing + * critical even if CPUID_HV_MSR_VP_INDEX is not set; keep + * moving on. + */ + VMBUS_PCPU_GET(sc, vcpuid, cpu) = cpu; + } + + /* + * Setup the SynIC message. + */ + orig = rdmsr(MSR_HV_SIMP); + val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) | + ((VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT) << + MSR_HV_SIMP_PGSHIFT); + wrmsr(MSR_HV_SIMP, val); + + /* + * Setup the SynIC event flags. + */ + orig = rdmsr(MSR_HV_SIEFP); + val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) | + ((VMBUS_PCPU_GET(sc, event_flags_dma.hv_paddr, cpu) + >> PAGE_SHIFT) << MSR_HV_SIEFP_PGSHIFT); + wrmsr(MSR_HV_SIEFP, val); + + + /* + * Configure and unmask SINT for message and event flags. + */ + sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE; + orig = rdmsr(sint); + val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI | + (orig & MSR_HV_SINT_RSVD_MASK); + wrmsr(sint, val); + + /* + * Configure and unmask SINT for timer. + */ + sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER; + orig = rdmsr(sint); + val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI | + (orig & MSR_HV_SINT_RSVD_MASK); + wrmsr(sint, val); + + /* + * All done; enable SynIC. + */ + orig = rdmsr(MSR_HV_SCONTROL); + val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK); + wrmsr(MSR_HV_SCONTROL, val); +} + +static void +vmbus_synic_teardown(void *arg) +{ + uint64_t orig; + uint32_t sint; + + /* + * Disable SynIC. + */ + orig = rdmsr(MSR_HV_SCONTROL); + wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK)); + + /* + * Mask message and event flags SINT. + */ + sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE; + orig = rdmsr(sint); + wrmsr(sint, orig | MSR_HV_SINT_MASKED); + + /* + * Mask timer SINT. + */ + sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER; + orig = rdmsr(sint); + wrmsr(sint, orig | MSR_HV_SINT_MASKED); + + /* + * Teardown SynIC message. + */ + orig = rdmsr(MSR_HV_SIMP); + wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK)); + + /* + * Teardown SynIC event flags. + */ + orig = rdmsr(MSR_HV_SIEFP); + wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK)); +} + +static int +vmbus_dma_alloc(struct vmbus_softc *sc) +{ + int cpu; + + CPU_FOREACH(cpu) { + void *ptr; + + /* + * Per-cpu messages and event flags. + */ + ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), + PAGE_SIZE, 0, PAGE_SIZE, + VMBUS_PCPU_PTR(sc, message_dma, cpu), + BUS_DMA_WAITOK | BUS_DMA_ZERO); + if (ptr == NULL) + return ENOMEM; + VMBUS_PCPU_GET(sc, message, cpu) = ptr; + + ptr = hyperv_dmamem_alloc(bus_get_dma_tag(sc->vmbus_dev), + PAGE_SIZE, 0, PAGE_SIZE, + VMBUS_PCPU_PTR(sc, event_flags_dma, cpu), + BUS_DMA_WAITOK | BUS_DMA_ZERO); + if (ptr == NULL) + return ENOMEM; + VMBUS_PCPU_GET(sc, event_flags, cpu) = ptr; + } + return 0; +} + +static void +vmbus_dma_free(struct vmbus_softc *sc) +{ + int cpu; + + CPU_FOREACH(cpu) { + if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) { + hyperv_dmamem_free( + VMBUS_PCPU_PTR(sc, message_dma, cpu), + VMBUS_PCPU_GET(sc, message, cpu)); + VMBUS_PCPU_GET(sc, message, cpu) = NULL; + } + if (VMBUS_PCPU_GET(sc, event_flags, cpu) != NULL) { + hyperv_dmamem_free( + VMBUS_PCPU_PTR(sc, event_flags_dma, cpu), + VMBUS_PCPU_GET(sc, event_flags, cpu)); + VMBUS_PCPU_GET(sc, event_flags, cpu) = NULL; + } + } +} + +static int +vmbus_intr_setup(struct vmbus_softc *sc) +{ + int cpu; + + CPU_FOREACH(cpu) { + char buf[MAXCOMLEN + 1]; + cpuset_t cpu_mask; + + /* Allocate an interrupt counter for Hyper-V interrupt */ + snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu); + intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu)); + + /* + * Setup taskqueue to handle events. Task will be per- + * channel. + */ + VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast( + "hyperv event", M_WAITOK, taskqueue_thread_enqueue, + VMBUS_PCPU_PTR(sc, event_tq, cpu)); + CPU_SETOF(cpu, &cpu_mask); + taskqueue_start_threads_cpuset( + VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask, + "hvevent%d", cpu); + + /* + * Setup tasks and taskqueues to handle messages. + */ + VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast( + "hyperv msg", M_WAITOK, taskqueue_thread_enqueue, + VMBUS_PCPU_PTR(sc, message_tq, cpu)); + CPU_SETOF(cpu, &cpu_mask); + taskqueue_start_threads_cpuset( + VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask, + "hvmsg%d", cpu); + TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0, + vmbus_msg_task, sc); + } + + /* + * All Hyper-V ISR required resources are setup, now let's find a + * free IDT vector for Hyper-V ISR and set it up. + */ + sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(vmbus_isr)); + if (sc->vmbus_idtvec < 0) { + device_printf(sc->vmbus_dev, "cannot find free IDT vector\n"); + return ENXIO; + } + if(bootverbose) { + device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n", + sc->vmbus_idtvec); + } + return 0; +} + +static void +vmbus_intr_teardown(struct vmbus_softc *sc) +{ + int cpu; + + if (sc->vmbus_idtvec >= 0) { + lapic_ipi_free(sc->vmbus_idtvec); + sc->vmbus_idtvec = -1; + } + + CPU_FOREACH(cpu) { + if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) { + taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu)); + VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL; + } + if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) { + taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu), + VMBUS_PCPU_PTR(sc, message_task, cpu)); + taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu)); + VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL; + } + } +} + +static int *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201606010920.u519Kqh6068360>