From e3c41896e9aa173644cc1e67e218562bae1ea69f Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Mon, 13 Jun 2016 09:58:01 +0800 Subject: [PATCH] hyperv/vmbus: Complete vmbus initialization; interrupt cputimer is enabled Most of the bits are obtained from FreeBSD. However, The interrupt bits are reworked: - Since the vmbus message/event interrupt works in the same fashion as MSI-X, we just allocate MSI-X for them, instead of allocating IDT vector, rolling vmbus own interrupt vector and turning the interrupt handling inside-out. The standard and generic bus APIs are used to allocate and setup per-cpu vmbus interrupt. - Interrupt cputimer reuses the current per-cpu interrupt timer code. - AutoEOI is not used, since we reuse the per-cpu interrupt timer IDT vector and MSI IDT vector. After a brief discussion w/ Dexuan Cui, I concluded that AutoEOI probably does not provide noticible performance improvement but will introduce extra code complexity. We leave it off for now. Obtained-from: FreeBSD (mostly) --- .../hyperv_var.h => include/hyperv_busdma.h} | 26 +- sys/dev/virtual/hyperv/vmbus/Makefile | 7 +- sys/dev/virtual/hyperv/vmbus/hyperv.c | 94 ++ .../hyperv/vmbus/{hyperv_var.h => hyperv_busdma.c} | 51 +- .../vmbus/{hyperv_var.h => hyperv_machdep.h} | 16 +- sys/dev/virtual/hyperv/vmbus/hyperv_var.h | 4 +- sys/dev/virtual/hyperv/vmbus/vmbus.c | 966 ++++++++++++++++++++- sys/dev/virtual/hyperv/vmbus/vmbus_reg.h | 129 +++ sys/dev/virtual/hyperv/vmbus/vmbus_var.h | 103 +++ .../{hyperv_var.h => x86_64/hyperv_machdep.c} | 29 +- sys/platform/pc64/include/msi_machdep.h | 3 + sys/platform/pc64/x86_64/msi.c | 1 - sys/sys/systimer.h | 2 + 13 files changed, 1382 insertions(+), 49 deletions(-) copy sys/dev/virtual/hyperv/{vmbus/hyperv_var.h => include/hyperv_busdma.h} (71%) copy sys/dev/virtual/hyperv/vmbus/{hyperv_var.h => hyperv_busdma.c} (57%) copy sys/dev/virtual/hyperv/vmbus/{hyperv_var.h => hyperv_machdep.h} (82%) create mode 100644 sys/dev/virtual/hyperv/vmbus/vmbus_reg.h create mode 100644 sys/dev/virtual/hyperv/vmbus/vmbus_var.h copy sys/dev/virtual/hyperv/vmbus/{hyperv_var.h => x86_64/hyperv_machdep.c} (71%) diff --git a/sys/dev/virtual/hyperv/vmbus/hyperv_var.h b/sys/dev/virtual/hyperv/include/hyperv_busdma.h similarity index 71% copy from sys/dev/virtual/hyperv/vmbus/hyperv_var.h copy to sys/dev/virtual/hyperv/include/hyperv_busdma.h index 6424d17c2a..625a33c28f 100644 --- a/sys/dev/virtual/hyperv/vmbus/hyperv_var.h +++ b/sys/dev/virtual/hyperv/include/hyperv_busdma.h @@ -26,15 +26,23 @@ * $FreeBSD$ */ -#ifndef _HYPERV_VAR_H_ -#define _HYPERV_VAR_H_ +#ifndef _HYPERV_BUSDMA_H_ +#define _HYPERV_BUSDMA_H_ -#ifndef NANOSEC -#define NANOSEC 1000000000ULL -#endif -#define HYPERV_TIMER_NS_FACTOR 100ULL -#define HYPERV_TIMER_FREQ (NANOSEC / HYPERV_TIMER_NS_FACTOR) +#include +#include -extern u_int hyperv_features; +struct hyperv_dma { + bus_addr_t hv_paddr; + bus_dma_tag_t hv_dtag; + bus_dmamap_t hv_dmap; +}; -#endif /* !_HYPERV_VAR_H_ */ +void hyperv_dma_map_paddr(void *arg, bus_dma_segment_t *segs, int nseg, + int error); +void *hyperv_dmamem_alloc(bus_dma_tag_t parent_dtag, bus_size_t alignment, + bus_addr_t boundary, bus_size_t size, struct hyperv_dma *dma, + int flags); +void hyperv_dmamem_free(struct hyperv_dma *dma, void *ptr); + +#endif /* !_HYPERV_BUSDMA_H_ */ diff --git a/sys/dev/virtual/hyperv/vmbus/Makefile b/sys/dev/virtual/hyperv/vmbus/Makefile index 0dfbd5aadd..d560adf77b 100644 --- a/sys/dev/virtual/hyperv/vmbus/Makefile +++ b/sys/dev/virtual/hyperv/vmbus/Makefile @@ -1,8 +1,11 @@ SYSDIR?=${.CURDIR}/../../../.. +.PATH: ${.CURDIR}/${MACHINE_ARCH} + KMOD= vmbus -SRCS= hyperv.c vmbus.c -SRCS+= acpi_if.h bus_if.h device_if.h +SRCS= hyperv.c hyperv_busdma.c hyperv_machdep.c +SRCS+= vmbus.c +SRCS+= acpi_if.h bus_if.h device_if.h pcib_if.h SRCS+= opt_acpi.h .include "../../../acpica/Makefile.inc" diff --git a/sys/dev/virtual/hyperv/vmbus/hyperv.c b/sys/dev/virtual/hyperv/vmbus/hyperv.c index 138ae8565c..eb52de76a3 100644 --- a/sys/dev/virtual/hyperv/vmbus/hyperv.c +++ b/sys/dev/virtual/hyperv/vmbus/hyperv.c @@ -33,6 +33,8 @@ #include +#include +#include #include #include @@ -55,10 +57,18 @@ 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); u_int hyperv_features; static u_int hyperv_recommends; @@ -80,6 +90,15 @@ static struct cputimer hyperv_cputimer = { 0, 0, 0 }; +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) { @@ -96,6 +115,72 @@ hyperv_cputimer_count(void) 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 boolean_t hyperv_identify(void) { @@ -192,6 +277,8 @@ hyperv_identify(void) 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) @@ -207,6 +294,12 @@ hyperv_init(void *dummy __unused) cputimer_register(&hyperv_cputimer); cputimer_select(&hyperv_cputimer, 0); } + + error = hypercall_create(); + if (error) { + /* Can't perform any Hyper-V specific actions */ + vmm_guest = VMM_GUEST_UNKNOWN; + } } SYSINIT(hyperv_initialize, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST, hyperv_init, NULL); @@ -218,6 +311,7 @@ hyperv_uninit(void *dummy __unused) /* Deregister Hyper-V systimer */ cputimer_deregister(&hyperv_cputimer); } + hypercall_destroy(); } SYSUNINIT(hyperv_uninitialize, SI_SUB_PRE_DRIVERS, SI_ORDER_FIRST, hyperv_uninit, NULL); diff --git a/sys/dev/virtual/hyperv/vmbus/hyperv_var.h b/sys/dev/virtual/hyperv/vmbus/hyperv_busdma.c similarity index 57% copy from sys/dev/virtual/hyperv/vmbus/hyperv_var.h copy to sys/dev/virtual/hyperv/vmbus/hyperv_busdma.c index 6424d17c2a..2356613854 100644 --- a/sys/dev/virtual/hyperv/vmbus/hyperv_var.h +++ b/sys/dev/virtual/hyperv/vmbus/hyperv_busdma.c @@ -22,19 +22,48 @@ * 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$ */ -#ifndef _HYPERV_VAR_H_ -#define _HYPERV_VAR_H_ +#include +#include +#include + +#include + +void +hyperv_dma_map_paddr(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + bus_addr_t *paddr = arg; + + if (error) + return; + + KASSERT(nseg == 1, ("too many segments %d!", nseg)); + *paddr = segs->ds_addr; +} + +void * +hyperv_dmamem_alloc(bus_dma_tag_t parent_dtag, bus_size_t alignment, + bus_addr_t boundary, bus_size_t size, struct hyperv_dma *dma, int flags) +{ + bus_dmamem_t dmem; + int error; -#ifndef NANOSEC -#define NANOSEC 1000000000ULL -#endif -#define HYPERV_TIMER_NS_FACTOR 100ULL -#define HYPERV_TIMER_FREQ (NANOSEC / HYPERV_TIMER_NS_FACTOR) + error = bus_dmamem_coherent(parent_dtag, alignment, boundary, + BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, size, flags, &dmem); + if (error) + return NULL; -extern u_int hyperv_features; + dma->hv_dtag = dmem.dmem_tag; + dma->hv_dmap = dmem.dmem_map; + dma->hv_paddr = dmem.dmem_busaddr; + return dmem.dmem_addr; +} -#endif /* !_HYPERV_VAR_H_ */ +void +hyperv_dmamem_free(struct hyperv_dma *dma, void *ptr) +{ + bus_dmamap_unload(dma->hv_dtag, dma->hv_dmap); + bus_dmamem_free(dma->hv_dtag, ptr, dma->hv_dmap); + bus_dma_tag_destroy(dma->hv_dtag); +} diff --git a/sys/dev/virtual/hyperv/vmbus/hyperv_var.h b/sys/dev/virtual/hyperv/vmbus/hyperv_machdep.h similarity index 82% copy from sys/dev/virtual/hyperv/vmbus/hyperv_var.h copy to sys/dev/virtual/hyperv/vmbus/hyperv_machdep.h index 6424d17c2a..b541f41912 100644 --- a/sys/dev/virtual/hyperv/vmbus/hyperv_var.h +++ b/sys/dev/virtual/hyperv/vmbus/hyperv_machdep.h @@ -26,15 +26,13 @@ * $FreeBSD$ */ -#ifndef _HYPERV_VAR_H_ -#define _HYPERV_VAR_H_ +#ifndef _HYPERV_MACHDEP_H_ +#define _HYPERV_MACHDEP_H_ -#ifndef NANOSEC -#define NANOSEC 1000000000ULL -#endif -#define HYPERV_TIMER_NS_FACTOR 100ULL -#define HYPERV_TIMER_FREQ (NANOSEC / HYPERV_TIMER_NS_FACTOR) +#include -extern u_int hyperv_features; +uint64_t hypercall_md(volatile void *hc_addr, uint64_t in_val, + uint64_t in_paddr, uint64_t out_paddr); +int hyperv_msi2vector(uint64_t msi_addr, uint32_t msi_data); -#endif /* !_HYPERV_VAR_H_ */ +#endif /* !_HYPERV_MACHDEP_H_ */ diff --git a/sys/dev/virtual/hyperv/vmbus/hyperv_var.h b/sys/dev/virtual/hyperv/vmbus/hyperv_var.h index 6424d17c2a..63dae7abf0 100644 --- a/sys/dev/virtual/hyperv/vmbus/hyperv_var.h +++ b/sys/dev/virtual/hyperv/vmbus/hyperv_var.h @@ -35,6 +35,8 @@ #define HYPERV_TIMER_NS_FACTOR 100ULL #define HYPERV_TIMER_FREQ (NANOSEC / HYPERV_TIMER_NS_FACTOR) -extern u_int hyperv_features; +extern u_int hyperv_features; + +uint64_t hypercall_post_message(bus_addr_t); #endif /* !_HYPERV_VAR_H_ */ diff --git a/sys/dev/virtual/hyperv/vmbus/vmbus.c b/sys/dev/virtual/hyperv/vmbus/vmbus.c index 9b03e5ab12..22e6b36cb2 100644 --- a/sys/dev/virtual/hyperv/vmbus/vmbus.c +++ b/sys/dev/virtual/hyperv/vmbus/vmbus.c @@ -32,16 +32,103 @@ #include #include #include +#include +#include +#include +#include +#include +#include + +#include +#include #include #include +#include +#include #include "acpi.h" #include "acpi_if.h" +#include "pcib_if.h" + +#define MSR_HV_STIMER0_CFG_SINT \ + ((((uint64_t)VMBUS_SINT_TIMER) << MSR_HV_STIMER_CFG_SINT_SHIFT) & \ + MSR_HV_STIMER_CFG_SINT_MASK) + +/* + * NOTE: DO NOT CHANGE THIS. + */ +#define VMBUS_SINT_MESSAGE 2 +/* + * NOTE: + * - DO NOT set it to the same value as VMBUS_SINT_MESSAGE. + * - DO NOT set it to 0. + */ +#define VMBUS_SINT_TIMER 4 + +/* + * NOTE: DO NOT CHANGE THESE + */ +#define VMBUS_CONNID_MESSAGE 1 +#define VMBUS_CONNID_EVENT 2 + +struct vmbus_msghc { + struct hypercall_postmsg_in *mh_inprm; + struct hypercall_postmsg_in mh_inprm_save; + struct hyperv_dma mh_inprm_dma; + + struct vmbus_message *mh_resp; + struct vmbus_message mh_resp0; +}; + +struct vmbus_msghc_ctx { + struct vmbus_msghc *mhc_free; + struct lwkt_token mhc_free_token; + uint32_t mhc_flags; + + struct vmbus_msghc *mhc_active; + struct lwkt_token mhc_active_token; +}; + +#define VMBUS_MSGHC_CTXF_DESTROY 0x0001 + +static int vmbus_probe(device_t); +static int vmbus_attach(device_t); +static int vmbus_detach(device_t); +static void vmbus_intr(void *); +static void vmbus_timer_intr_reload(struct cputimer_intr *, + sysclock_t); +static void vmbus_timer_intr_pcpuhand( + struct cputimer_intr *); +static void vmbus_timer_intr_restart( + struct cputimer_intr *); -static int vmbus_probe(device_t); -static int vmbus_attach(device_t); -static int vmbus_detach(device_t); +static int vmbus_dma_alloc(struct vmbus_softc *); +static void vmbus_dma_free(struct vmbus_softc *); +static int vmbus_intr_setup(struct vmbus_softc *); +static void vmbus_intr_teardown(struct vmbus_softc *); +static int vmbus_intr_rid(struct resource_list *, int); +static void vmbus_synic_setup(void *); +static void vmbus_synic_teardown(void *); +static void vmbus_timer_stop(void *); +static void vmbus_timer_config(void *); +static int vmbus_init(struct vmbus_softc *); +static int vmbus_init_contact(struct vmbus_softc *, + uint32_t); +static void vmbus_timer_restart(void *); +static void vmbus_timer_msgintr(struct vmbus_pcpu_data *); + +static void vmbus_chan_msgproc(struct vmbus_softc *, + const struct vmbus_message *); + +static struct vmbus_msghc_ctx *vmbus_msghc_ctx_create(bus_dma_tag_t); +static void vmbus_msghc_ctx_destroy( + struct vmbus_msghc_ctx *); +static void vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *); +static struct vmbus_msghc *vmbus_msghc_alloc(bus_dma_tag_t); +static void vmbus_msghc_free(struct vmbus_msghc *); +static struct vmbus_msghc *vmbus_msghc_get1(struct vmbus_msghc_ctx *, + uint32_t); static device_method_t vmbus_methods[] = { /* Device interface */ @@ -58,7 +145,7 @@ static device_method_t vmbus_methods[] = { static driver_t vmbus_driver = { "vmbus", vmbus_methods, - 0 + sizeof(struct vmbus_softc) }; static devclass_t vmbus_devclass; @@ -67,6 +154,33 @@ DRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL); MODULE_DEPEND(vmbus, acpi, 1, 1, 1); MODULE_VERSION(vmbus, 1); +static struct cputimer_intr vmbus_cputimer_intr = { + .freq = HYPERV_TIMER_FREQ, + .reload = vmbus_timer_intr_reload, + .enable = cputimer_intr_default_enable, + .config = cputimer_intr_default_config, + .restart = vmbus_timer_intr_restart, + .pmfixup = cputimer_intr_default_pmfixup, + .initclock = cputimer_intr_default_initclock, + .pcpuhand = vmbus_timer_intr_pcpuhand, + .next = SLIST_ENTRY_INITIALIZER, + .name = "hyperv", + .type = CPUTIMER_INTR_VMM, + .prio = CPUTIMER_INTR_PRIO_VMM, + .caps = CPUTIMER_INTR_CAP_PS, + .priv = NULL +}; + +static const uint32_t vmbus_version[] = { + VMBUS_VERSION_WIN8_1, + VMBUS_VERSION_WIN8, + VMBUS_VERSION_WIN7, + VMBUS_VERSION_WS2008 +}; + +static int vmbus_timer_intr_enable = 1; +TUNABLE_INT("hw.vmbus.timer_intr.enable", &vmbus_timer_intr_enable); + static int vmbus_probe(device_t dev) { @@ -85,11 +199,853 @@ vmbus_probe(device_t dev) static int vmbus_attach(device_t dev) { - return (0); + struct vmbus_softc *sc = device_get_softc(dev); + int error, cpu, unit; + + sc->vmbus_dev = dev; + for (cpu = 0; cpu < ncpus; ++cpu) { + struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu); + + psc->sc = sc; + psc->cpuid = cpu; + psc->timer_last = UINT64_MAX; + } + unit = device_get_unit(dev); + + /* + * Create context for "post message" Hypercalls + */ + sc->vmbus_msg_hc = vmbus_msghc_ctx_create( + bus_get_dma_tag(sc->vmbus_dev)); + if (sc->vmbus_msg_hc == NULL) + return ENXIO; + + /* + * Allocate DMA stuffs. + */ + error = vmbus_dma_alloc(sc); + if (error) + goto failed; + + /* + * Setup interrupt. + */ + error = vmbus_intr_setup(sc); + if (error) + goto failed; + + if (unit == 0) { + /* + * Make sure timer is stopped. + */ + lwkt_cpusync_simple(smp_active_mask, vmbus_timer_stop, sc); + } + + /* + * Setup SynIC. + */ + lwkt_cpusync_simple(smp_active_mask, vmbus_synic_setup, sc); + sc->vmbus_flags |= VMBUS_FLAG_SYNIC; + + /* + * Initialize vmbus. + */ + error = vmbus_init(sc); + if (error) + goto failed; + + if (unit == 0) { + /* + * Configure and register vmbus interrupt cputimer. + */ + lwkt_cpusync_simple(smp_active_mask, vmbus_timer_config, sc); + vmbus_cputimer_intr.priv = sc; + cputimer_intr_register(&vmbus_cputimer_intr); + if (vmbus_timer_intr_enable) + cputimer_intr_select(&vmbus_cputimer_intr, 0); + } + + return 0; +failed: + vmbus_detach(dev); + return error; } static int vmbus_detach(device_t dev) { + struct vmbus_softc *sc = device_get_softc(dev); + + /* TODO: uninitialize vmbus. */ + /* TODO: stop and deregister timer */ + + if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) + lwkt_cpusync_simple(smp_active_mask, vmbus_synic_teardown, sc); + vmbus_intr_teardown(sc); + vmbus_dma_free(sc); + + if (sc->vmbus_msg_hc != NULL) { + vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc); + sc->vmbus_msg_hc = NULL; + } return (0); } + +static void +vmbus_intr(void *xpsc) +{ + struct vmbus_pcpu_data *psc = xpsc; + volatile struct vmbus_message *msg; + + msg = psc->message + VMBUS_SINT_MESSAGE; + while (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) { + if (msg->msg_type == HYPERV_MSGTYPE_CHANNEL) { + /* Channel message */ + vmbus_chan_msgproc(psc->sc, + __DEVOLATILE(const struct vmbus_message *, msg)); + } + + msg->msg_type = HYPERV_MSGTYPE_NONE; + /* + * Make sure the write to msg_type (i.e. set to + * HYPERV_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 + */ + cpu_mfence(); + 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 void +vmbus_timer_oneshot(struct vmbus_pcpu_data *psc, uint64_t current) +{ + psc->timer_last = current; + wrmsr(MSR_HV_STIMER0_COUNT, current); +} + +static void +vmbus_timer_intr_reload(struct cputimer_intr *cti, sysclock_t reload) +{ + struct globaldata *gd = mycpu; + struct vmbus_softc *sc = cti->priv; + struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, gd->gd_cpuid); + uint64_t current; + + reload = (uint64_t)reload * cti->freq / sys_cputimer->freq; + current = rdmsr(MSR_HV_TIME_REF_COUNT) + reload; + + if (gd->gd_timer_running) { + if (current < psc->timer_last) + vmbus_timer_oneshot(psc, current); + } else { + gd->gd_timer_running = 1; + vmbus_timer_oneshot(psc, current); + } +} + +static void +vmbus_timer_intr_pcpuhand(struct cputimer_intr *cti) +{ + struct vmbus_softc *sc = cti->priv; + struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, mycpuid); + + vmbus_timer_msgintr(psc); +} + +static void +vmbus_timer_intr_restart(struct cputimer_intr *cti) +{ + lwkt_send_ipiq_mask(smp_active_mask, vmbus_timer_restart, cti->priv); +} + +static struct vmbus_msghc * +vmbus_msghc_alloc(bus_dma_tag_t parent_dtag) +{ + struct vmbus_msghc *mh; + + mh = kmalloc(sizeof(*mh), M_DEVBUF, M_WAITOK | M_ZERO); + + mh->mh_inprm = hyperv_dmamem_alloc(parent_dtag, + HYPERCALL_POSTMSGIN_ALIGN, 0, HYPERCALL_POSTMSGIN_SIZE, + &mh->mh_inprm_dma, BUS_DMA_WAITOK); + if (mh->mh_inprm == NULL) { + kfree(mh, M_DEVBUF); + return NULL; + } + return mh; +} + +static void +vmbus_msghc_free(struct vmbus_msghc *mh) +{ + hyperv_dmamem_free(&mh->mh_inprm_dma, mh->mh_inprm); + kfree(mh, M_DEVBUF); +} + +static void +vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *mhc) +{ + KASSERT(mhc->mhc_active == NULL, ("still have active msg hypercall")); + KASSERT(mhc->mhc_free == NULL, ("still have hypercall msg")); + + lwkt_token_uninit(&mhc->mhc_free_token); + lwkt_token_uninit(&mhc->mhc_active_token); + kfree(mhc, M_DEVBUF); +} + +static struct vmbus_msghc_ctx * +vmbus_msghc_ctx_create(bus_dma_tag_t parent_dtag) +{ + struct vmbus_msghc_ctx *mhc; + + mhc = kmalloc(sizeof(*mhc), M_DEVBUF, M_WAITOK | M_ZERO); + lwkt_token_init(&mhc->mhc_free_token, "msghcf"); + lwkt_token_init(&mhc->mhc_active_token, "msghca"); + + mhc->mhc_free = vmbus_msghc_alloc(parent_dtag); + if (mhc->mhc_free == NULL) { + vmbus_msghc_ctx_free(mhc); + return NULL; + } + return mhc; +} + +static struct vmbus_msghc * +vmbus_msghc_get1(struct vmbus_msghc_ctx *mhc, uint32_t dtor_flag) +{ + struct vmbus_msghc *mh; + + lwkt_gettoken(&mhc->mhc_free_token); + + while ((mhc->mhc_flags & dtor_flag) == 0 && mhc->mhc_free == NULL) + tsleep(&mhc->mhc_free, 0, "gmsghc", 0); + if (mhc->mhc_flags & dtor_flag) { + /* Being destroyed */ + mh = NULL; + } else { + mh = mhc->mhc_free; + KASSERT(mh != NULL, ("no free hypercall msg")); + KASSERT(mh->mh_resp == NULL, + ("hypercall msg has pending response")); + mhc->mhc_free = NULL; + } + + lwkt_reltoken(&mhc->mhc_free_token); + + return mh; +} + +struct vmbus_msghc * +vmbus_msghc_get(struct vmbus_softc *sc, size_t dsize) +{ + struct hypercall_postmsg_in *inprm; + struct vmbus_msghc *mh; + + if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX) + return NULL; + + mh = vmbus_msghc_get1(sc->vmbus_msg_hc, VMBUS_MSGHC_CTXF_DESTROY); + if (mh == NULL) + return NULL; + + inprm = mh->mh_inprm; + memset(inprm, 0, HYPERCALL_POSTMSGIN_SIZE); + inprm->hc_connid = VMBUS_CONNID_MESSAGE; + inprm->hc_msgtype = HYPERV_MSGTYPE_CHANNEL; + inprm->hc_dsize = dsize; + + return mh; +} + +void +vmbus_msghc_put(struct vmbus_softc *sc, struct vmbus_msghc *mh) +{ + struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc; + + KASSERT(mhc->mhc_active == NULL, ("msg hypercall is active")); + mh->mh_resp = NULL; + + lwkt_gettoken(&mhc->mhc_free_token); + KASSERT(mhc->mhc_free == NULL, ("has free hypercall msg")); + mhc->mhc_free = mh; + lwkt_reltoken(&mhc->mhc_free_token); + wakeup(&mhc->mhc_free); +} + +void * +vmbus_msghc_dataptr(struct vmbus_msghc *mh) +{ + return mh->mh_inprm->hc_data; +} + +static void +vmbus_msghc_ctx_destroy(struct vmbus_msghc_ctx *mhc) +{ + struct vmbus_msghc *mh; + + lwkt_gettoken(&mhc->mhc_free_token); + mhc->mhc_flags |= VMBUS_MSGHC_CTXF_DESTROY; + lwkt_reltoken(&mhc->mhc_free_token); + wakeup(&mhc->mhc_free); + + mh = vmbus_msghc_get1(mhc, 0); + if (mh == NULL) + panic("can't get msghc"); + + vmbus_msghc_free(mh); + vmbus_msghc_ctx_free(mhc); +} + +int +vmbus_msghc_exec_noresult(struct vmbus_msghc *mh) +{ + int i, wait_ticks = 1; + + /* + * Save the input parameter so that we could restore the input + * parameter if the Hypercall failed. + * + * XXX + * Is this really necessary?! i.e. Will the Hypercall ever + * overwrite the input parameter? + */ + memcpy(&mh->mh_inprm_save, mh->mh_inprm, HYPERCALL_POSTMSGIN_SIZE); + + /* + * In order to cope with transient failures, e.g. insufficient + * resources on host side, we retry the post message Hypercall + * several times. 20 retries seem sufficient. + */ +#define HC_RETRY_MAX 20 + + for (i = 0; i < HC_RETRY_MAX; ++i) { + uint64_t status; + + status = hypercall_post_message(mh->mh_inprm_dma.hv_paddr); + if (status == HYPERCALL_STATUS_SUCCESS) + return 0; + + tsleep(&status, 0, "hcpmsg", wait_ticks); + if (wait_ticks < hz) + wait_ticks *= 2; + + /* Restore input parameter and try again */ + memcpy(mh->mh_inprm, &mh->mh_inprm_save, + HYPERCALL_POSTMSGIN_SIZE); + } + +#undef HC_RETRY_MAX + + return EIO; +} + +int +vmbus_msghc_exec(struct vmbus_softc *sc, struct vmbus_msghc *mh) +{ + struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc; + int error; + + KASSERT(mh->mh_resp == NULL, ("hypercall msg has pending response")); + + lwkt_gettoken(&mhc->mhc_active_token); + KASSERT(mhc->mhc_active == NULL, ("pending active msg hypercall")); + mhc->mhc_active = mh; + lwkt_reltoken(&mhc->mhc_active_token); + + error = vmbus_msghc_exec_noresult(mh); + if (error) { + lwkt_gettoken(&mhc->mhc_active_token); + KASSERT(mhc->mhc_active == mh, ("msghc mismatch")); + mhc->mhc_active = NULL; + lwkt_reltoken(&mhc->mhc_active_token); + } + return error; +} + +const struct vmbus_message * +vmbus_msghc_wait_result(struct vmbus_softc *sc, struct vmbus_msghc *mh) +{ + struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc; + + lwkt_gettoken(&mhc->mhc_active_token); + + KASSERT(mhc->mhc_active == mh, ("msghc mismatch")); + while (mh->mh_resp == NULL) + tsleep(&mhc->mhc_active, 0, "wmsghc", 0); + mhc->mhc_active = NULL; + + lwkt_reltoken(&mhc->mhc_active_token); + + return mh->mh_resp; +} + +void +vmbus_msghc_wakeup(struct vmbus_softc *sc, const struct vmbus_message *msg) +{ + struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc; + struct vmbus_msghc *mh; + + lwkt_gettoken(&mhc->mhc_active_token); + + mh = mhc->mhc_active; + KASSERT(mh != NULL, ("no pending msg hypercall")); + memcpy(&mh->mh_resp0, msg, sizeof(mh->mh_resp0)); + mh->mh_resp = &mh->mh_resp0; + + lwkt_reltoken(&mhc->mhc_active_token); + wakeup(&mhc->mhc_active); +} + +static int +vmbus_dma_alloc(struct vmbus_softc *sc) +{ + bus_dma_tag_t parent_dtag; + uint8_t *evtflags; + int cpu; + + parent_dtag = bus_get_dma_tag(sc->vmbus_dev); + for (cpu = 0; cpu < ncpus; ++cpu) { + struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu); + + /* + * Per-cpu messages and event flags. + */ + psc->message = hyperv_dmamem_alloc(parent_dtag, + PAGE_SIZE, 0, PAGE_SIZE, &psc->message_dma, + BUS_DMA_WAITOK | BUS_DMA_ZERO); + if (psc->message == NULL) + return ENOMEM; + + psc->event_flags = hyperv_dmamem_alloc(parent_dtag, + PAGE_SIZE, 0, PAGE_SIZE, &psc->event_flags_dma, + BUS_DMA_WAITOK | BUS_DMA_ZERO); + if (psc->event_flags == NULL) + return ENOMEM; + } + + evtflags = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, + PAGE_SIZE, &sc->vmbus_evtflags_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); + if (evtflags == NULL) + return ENOMEM; + sc->vmbus_rx_evtflags = (u_long *)evtflags; + sc->vmbus_tx_evtflags = (u_long *)(evtflags + (PAGE_SIZE / 2)); + sc->vmbus_evtflags = evtflags; + + sc->vmbus_mnf1 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, + PAGE_SIZE, &sc->vmbus_mnf1_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); + if (sc->vmbus_mnf1 == NULL) + return ENOMEM; + + sc->vmbus_mnf2 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, + PAGE_SIZE, &sc->vmbus_mnf2_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); + if (sc->vmbus_mnf2 == NULL) + return ENOMEM; + + return 0; +} + +static void +vmbus_dma_free(struct vmbus_softc *sc) +{ + int cpu; + + if (sc->vmbus_evtflags != NULL) { + hyperv_dmamem_free(&sc->vmbus_evtflags_dma, sc->vmbus_evtflags); + sc->vmbus_evtflags = NULL; + sc->vmbus_rx_evtflags = NULL; + sc->vmbus_tx_evtflags = NULL; + } + if (sc->vmbus_mnf1 != NULL) { + hyperv_dmamem_free(&sc->vmbus_mnf1_dma, sc->vmbus_mnf1); + sc->vmbus_mnf1 = NULL; + } + if (sc->vmbus_mnf2 != NULL) { + hyperv_dmamem_free(&sc->vmbus_mnf2_dma, sc->vmbus_mnf2); + sc->vmbus_mnf2 = NULL; + } + + for (cpu = 0; cpu < ncpus; ++cpu) { + struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu); + + if (psc->message != NULL) { + hyperv_dmamem_free(&psc->message_dma, psc->message); + psc->message = NULL; + } + if (psc->event_flags != NULL) { + hyperv_dmamem_free(&psc->event_flags_dma, + psc->event_flags); + psc->event_flags = NULL; + } + } +} + +static int +vmbus_intr_rid(struct resource_list *rl, int rid) +{ + do { + ++rid; + if (resource_list_find(rl, SYS_RES_IRQ, rid) == NULL) + break; + } while (1); + return rid; +} + +static int +vmbus_intr_setup(struct vmbus_softc *sc) +{ + device_t dev = sc->vmbus_dev; + device_t parent = device_get_parent(dev); + device_t bus = device_get_parent(parent); + struct resource_list *rl; + int rid, cpu; + + rl = BUS_GET_RESOURCE_LIST(parent, dev); + if (rl == NULL) + return ENXIO; + + rid = 0; + for (cpu = 0; cpu < ncpus; ++cpu) { + struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu); + struct resource *res; + uint64_t msi_addr; + uint32_t msi_data; + int irq, error; + + error = PCIB_ALLOC_MSIX(bus, dev, &irq, cpu); + if (error) { + device_printf(dev, "alloc vector on cpu%d failed: %d\n", + cpu, error); + return ENXIO; + } + rid = vmbus_intr_rid(rl, rid); + resource_list_add(rl, SYS_RES_IRQ, rid, irq, irq, 1, cpu); + psc->intr_rid = rid; + + res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (res == NULL) { + device_printf(dev, "alloc irq on cpu%d failed: %d\n", + cpu, error); + return ENXIO; + } + psc->intr_res = res; + + error = PCIB_MAP_MSI(bus, dev, rman_get_start(res), + &msi_addr, &msi_data, cpu); + if (error) { + device_printf(dev, "map irq on cpu%d failed: %d\n", + cpu, error); + return ENXIO; + } + psc->intr_vec = hyperv_msi2vector(msi_addr, msi_data); + + if (bootverbose) { + device_printf(dev, "vector %d irq %d on cpu%d\n", + psc->intr_vec, irq, cpu); + } + + ksnprintf(psc->intr_desc, sizeof(psc->intr_desc), "%s cpu%d", + device_get_nameunit(dev), cpu); + error = bus_setup_intr_descr(dev, res, INTR_MPSAFE, vmbus_intr, + psc, &psc->intr_hand, NULL, psc->intr_desc); + if (error) { + device_printf(dev, "setup intr on cpu%d failed: %d\n", + cpu, error); + return ENXIO; + } + } + return 0; +} + +static void +vmbus_intr_teardown(struct vmbus_softc *sc) +{ + device_t dev = sc->vmbus_dev; + device_t parent = device_get_parent(dev); + struct resource_list *rl; + int cpu; + + rl = BUS_GET_RESOURCE_LIST(parent, dev); + if (rl == NULL) + return; + + for (cpu = 0; cpu < ncpus; ++cpu) { + struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu); + + if (psc->intr_hand != NULL) { + bus_teardown_intr(dev, psc->intr_res, psc->intr_hand); + psc->intr_hand = NULL; + } + + if (psc->intr_res != NULL) { + bus_release_resource(dev, SYS_RES_IRQ, psc->intr_rid, + psc->intr_res); + psc->intr_res = NULL; + } + + if (psc->intr_rid != 0) { + struct resource_list_entry *rle; + int irq; + + rle = resource_list_find(rl, SYS_RES_IRQ, + psc->intr_rid); + irq = rle->start; + resource_list_delete(rl, SYS_RES_IRQ, psc->intr_rid); + + PCIB_RELEASE_MSIX(device_get_parent(parent), dev, irq, + psc->cpuid); + psc->intr_rid = 0; + } + } +} + +static void +vmbus_synic_setup(void *xsc) +{ + struct vmbus_softc *sc = xsc; + struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, mycpuid); + uint64_t val, orig; + uint32_t sint; + + if (hyperv_features & CPUID_HV_MSR_VP_INDEX) { + /* + * Save virtual processor id. + */ + psc->vcpuid = 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. + */ + psc->vcpuid = mycpuid; + } + + /* + * Setup the SynIC message. + */ + orig = rdmsr(MSR_HV_SIMP); + val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) | + ((psc->message_dma.hv_paddr >> 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) | + ((psc->event_flags_dma.hv_paddr >> 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 = psc->intr_vec | /* MSR_HV_SINT_AUTOEOI | notyet */ + (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 = XTIMER_OFFSET | /* MSR_HV_SINT_AUTOEOI | notyet */ + (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_timer_stop(void *arg __unused) +{ + for (;;) { + uint64_t val; + + /* Stop counting, and this also implies disabling STIMER0 */ + wrmsr(MSR_HV_STIMER0_COUNT, 0); + + val = rdmsr(MSR_HV_STIMER0_CONFIG); + if ((val & MSR_HV_STIMER_CFG_ENABLE) == 0) + break; + cpu_pause(); + } +} + +static void +vmbus_timer_config(void *arg __unused) +{ + /* + * Make sure that STIMER0 is really disabled before writing + * to STIMER0_CONFIG. + * + * "Writing to the configuration register of a timer that + * is already enabled may result in undefined behaviour." + */ + vmbus_timer_stop(arg); + wrmsr(MSR_HV_STIMER0_CONFIG, + MSR_HV_STIMER_CFG_AUTOEN | MSR_HV_STIMER0_CFG_SINT); +} + +static void +vmbus_timer_msgintr(struct vmbus_pcpu_data *psc) +{ + volatile struct vmbus_message *msg; + + msg = psc->message + VMBUS_SINT_TIMER; + if (msg->msg_type == HYPERV_MSGTYPE_TIMER_EXPIRED) { + msg->msg_type = HYPERV_MSGTYPE_NONE; + + /* + * Make sure the write to msg_type (i.e. set to + * HYPERV_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. + */ + cpu_mfence(); + 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 void +vmbus_timer_restart(void *xsc) +{ + struct vmbus_softc *sc = xsc; + struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, mycpuid); + + crit_enter(); + vmbus_timer_msgintr(psc); + vmbus_timer_oneshot(psc, rdmsr(MSR_HV_TIME_REF_COUNT) + 1); + crit_exit(); +} + +static void +vmbus_synic_teardown(void *arg __unused) +{ + 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_init_contact(struct vmbus_softc *sc, uint32_t version) +{ + struct vmbus_chanmsg_init_contact *req; + const struct vmbus_chanmsg_version_resp *resp; + const struct vmbus_message *msg; + struct vmbus_msghc *mh; + int error, supp = 0; + + mh = vmbus_msghc_get(sc, sizeof(*req)); + if (mh == NULL) + return ENXIO; + + req = vmbus_msghc_dataptr(mh); + req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_INIT_CONTACT; + req->chm_ver = version; + req->chm_evtflags = sc->vmbus_evtflags_dma.hv_paddr; + req->chm_mnf1 = sc->vmbus_mnf1_dma.hv_paddr; + req->chm_mnf2 = sc->vmbus_mnf2_dma.hv_paddr; + + error = vmbus_msghc_exec(sc, mh); + if (error) { + vmbus_msghc_put(sc, mh); + return error; + } + + msg = vmbus_msghc_wait_result(sc, mh); + resp = (const struct vmbus_chanmsg_version_resp *)msg->msg_data; + supp = resp->chm_supp; + + vmbus_msghc_put(sc, mh); + + return (supp ? 0 : EOPNOTSUPP); +} + +static int +vmbus_init(struct vmbus_softc *sc) +{ + int i; + + for (i = 0; i < nitems(vmbus_version); ++i) { + int error; + + error = vmbus_init_contact(sc, vmbus_version[i]); + if (!error) { + sc->vmbus_version = vmbus_version[i]; + device_printf(sc->vmbus_dev, "version %u.%u\n", + (sc->vmbus_version >> 16), + (sc->vmbus_version & 0xffff)); + return 0; + } + } + return ENXIO; +} + +static void +vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg) +{ + const struct vmbus_chanmsg_hdr *hdr; + + hdr = (const struct vmbus_chanmsg_hdr *)msg->msg_data; + + /* TODO */ + if (hdr->chm_type == VMBUS_CHANMSG_TYPE_VERSION_RESP) + vmbus_msghc_wakeup(sc, msg); +} diff --git a/sys/dev/virtual/hyperv/vmbus/vmbus_reg.h b/sys/dev/virtual/hyperv/vmbus/vmbus_reg.h new file mode 100644 index 0000000000..5ce2c9a24c --- /dev/null +++ b/sys/dev/virtual/hyperv/vmbus/vmbus_reg.h @@ -0,0 +1,129 @@ +/*- + * 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$ + */ + +#ifndef _VMBUS_REG_H_ +#define _VMBUS_REG_H_ + +#include + +/* + * Hyper-V SynIC message format. + */ + +#define VMBUS_MSG_DSIZE_MAX 240 +#define VMBUS_MSG_SIZE 256 + +struct vmbus_message { + uint32_t msg_type; /* HYPERV_MSGTYPE_ */ + uint8_t msg_dsize; /* data size */ + uint8_t msg_flags; /* VMBUS_MSGFLAG_ */ + uint16_t msg_rsvd; + uint64_t msg_id; + uint8_t msg_data[VMBUS_MSG_DSIZE_MAX]; +} __packed; +CTASSERT(sizeof(struct vmbus_message) == VMBUS_MSG_SIZE); + +#define VMBUS_MSGFLAG_PENDING 0x01 + +/* + * Hyper-V SynIC event flags + */ + +#ifdef __LP64__ +#define VMBUS_EVTFLAGS_MAX 32 +#define VMBUS_EVTFLAG_SHIFT 6 +#else +#define VMBUS_EVTFLAGS_MAX 64 +#define VMBUS_EVTFLAG_SHIFT 5 +#endif +#define VMBUS_EVTFLAG_LEN (1 << VMBUS_EVTFLAG_SHIFT) +#define VMBUS_EVTFLAG_MASK (VMBUS_EVTFLAG_LEN - 1) +#define VMBUS_EVTFLAGS_SIZE 256 + +struct vmbus_evtflags { + u_long evt_flags[VMBUS_EVTFLAGS_MAX]; +} __packed; +CTASSERT(sizeof(struct vmbus_evtflags) == VMBUS_EVTFLAGS_SIZE); + +/* + * Hyper-V vmbus version + * + * 0.13 -- Windows Server 2008 + * 1.1 -- Windows 7 + * 2.4 -- Windows 8 + * 3.0 -- Windows 8.1 + */ +#define VMBUS_VERSION_WS2008 ((0 << 16) | 13) +#define VMBUS_VERSION_WIN7 ((1 << 16) | 1) +#define VMBUS_VERSION_WIN8 ((2 << 16) | 4) +#define VMBUS_VERSION_WIN8_1 ((3 << 16) | 0) + +/* + * Channel + */ + +#define VMBUS_CHAN_MAX_COMPAT 256 +#define VMBUS_CHAN_MAX (VMBUS_EVTFLAG_LEN * VMBUS_EVTFLAGS_MAX) + +/* + * Channel messages + * - Embedded in vmbus_message.msg_data, e.g. response. + * - Embedded in hypercall_postmsg_in.hc_data, e.g. request. + */ + +#define VMBUS_CHANMSG_TYPE_CHANNEL_REQ 3 /* REQ */ +#define VMBUS_CHANMSG_TYPE_INIT_CONTACT 14 /* REQ */ +#define VMBUS_CHANMSG_TYPE_VERSION_RESP 15 /* RESP */ + +struct vmbus_chanmsg_hdr { + uint32_t chm_type; /* VMBUS_CHANMSG_TYPE_ */ + uint32_t chm_rsvd; +} __packed; + +/* VMBUS_CHANMSG_TYPE_INIT_CONTACT */ +struct vmbus_chanmsg_init_contact { + struct vmbus_chanmsg_hdr chm_hdr; + uint32_t chm_ver; + uint32_t chm_rsvd; + uint64_t chm_evtflags; + uint64_t chm_mnf1; + uint64_t chm_mnf2; +} __packed; + +/* VMBUS_CHANMSG_TYPE_VERSION_RESP */ +struct vmbus_chanmsg_version_resp { + struct vmbus_chanmsg_hdr chm_hdr; + uint8_t chm_supp; +} __packed; + +/* VMBUS_CHANMSG_TYPE_CHANNEL_REQ */ +struct vmbus_chanmsg_channel_req { + struct vmbus_chanmsg_hdr chm_hdr; +} __packed; + +#endif /* !_VMBUS_REG_H_ */ diff --git a/sys/dev/virtual/hyperv/vmbus/vmbus_var.h b/sys/dev/virtual/hyperv/vmbus/vmbus_var.h new file mode 100644 index 0000000000..accfc476ef --- /dev/null +++ b/sys/dev/virtual/hyperv/vmbus/vmbus_var.h @@ -0,0 +1,103 @@ +/*- + * 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$ + */ + +#ifndef _VMBUS_VAR_H_ +#define _VMBUS_VAR_H_ + +#include + +struct vmbus_pcpu_data { + struct vmbus_message *message; /* shared messages */ + int event_flags_cnt;/* # of event flags */ + struct vmbus_evtflags *event_flags; /* event flags from host */ + + struct vmbus_softc *sc; + int cpuid; + int vcpuid; + + uint64_t timer_last; + + /* + * Rarely used fields + */ + + struct hyperv_dma message_dma; /* busdma glue */ + struct hyperv_dma event_flags_dma;/* busdma glue */ + + /* Interrupt stuffs */ + void *intr_hand; + struct resource *intr_res; + int intr_rid; + int intr_vec; + char intr_desc[24]; +} __cachealign; + +struct vmbus_softc { + u_long *vmbus_tx_evtflags; + /* event flags to host */ + void *vmbus_mnf2; /* monitored by host */ + + u_long *vmbus_rx_evtflags; + /* compat evtflgs from host */ + struct vmbus_pcpu_data vmbus_pcpu[MAXCPU]; + + /* + * Rarely used fields + */ + + device_t vmbus_dev; + struct vmbus_msghc_ctx *vmbus_msg_hc; + uint32_t vmbus_flags; /* see VMBUS_FLAG_ */ + uint32_t vmbus_version; /* VMBUS_VERSION_ */ + + /* Shared memory for vmbus_{rx,tx}_evtflags */ + void *vmbus_evtflags; + struct hyperv_dma vmbus_evtflags_dma; + + void *vmbus_mnf1; /* monitored by VM, unused */ + struct hyperv_dma vmbus_mnf1_dma; + struct hyperv_dma vmbus_mnf2_dma; +}; + +#define VMBUS_FLAG_SYNIC 0x0002 /* SynIC was setup */ + +#define VMBUS_PCPU(sc, cpu) &(sc)->vmbus_pcpu[(cpu)] + +struct vmbus_message; +struct vmbus_msghc; + +struct vmbus_msghc *vmbus_msghc_get(struct vmbus_softc *, size_t); +void vmbus_msghc_put(struct vmbus_softc *, struct vmbus_msghc *); +void *vmbus_msghc_dataptr(struct vmbus_msghc *); +int vmbus_msghc_exec_noresult(struct vmbus_msghc *); +int vmbus_msghc_exec(struct vmbus_softc *, struct vmbus_msghc *); +const struct vmbus_message *vmbus_msghc_wait_result(struct vmbus_softc *, + struct vmbus_msghc *); +void vmbus_msghc_wakeup(struct vmbus_softc *, const struct vmbus_message *); + +#endif /* !_VMBUS_VAR_H_ */ diff --git a/sys/dev/virtual/hyperv/vmbus/hyperv_var.h b/sys/dev/virtual/hyperv/vmbus/x86_64/hyperv_machdep.c similarity index 71% copy from sys/dev/virtual/hyperv/vmbus/hyperv_var.h copy to sys/dev/virtual/hyperv/vmbus/x86_64/hyperv_machdep.c index 6424d17c2a..dd824d9073 100644 --- a/sys/dev/virtual/hyperv/vmbus/hyperv_var.h +++ b/sys/dev/virtual/hyperv/vmbus/x86_64/hyperv_machdep.c @@ -22,19 +22,26 @@ * 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$ */ -#ifndef _HYPERV_VAR_H_ -#define _HYPERV_VAR_H_ +#include +#include +#include -#ifndef NANOSEC -#define NANOSEC 1000000000ULL -#endif -#define HYPERV_TIMER_NS_FACTOR 100ULL -#define HYPERV_TIMER_FREQ (NANOSEC / HYPERV_TIMER_NS_FACTOR) +uint64_t +hypercall_md(volatile void *hc_addr, uint64_t in_val, + uint64_t in_paddr, uint64_t out_paddr) +{ + uint64_t status; -extern u_int hyperv_features; + __asm__ __volatile__ ("mov %0, %%r8" : : "r" (out_paddr): "r8"); + __asm__ __volatile__ ("call *%3" : "=a" (status) : + "c" (in_val), "d" (in_paddr), "m" (hc_addr)); + return (status); +} -#endif /* !_HYPERV_VAR_H_ */ +int +hyperv_msi2vector(uint64_t msi_addr __unused, uint32_t msi_data) +{ + return (msi_data & MSI_X86_DATA_INTVEC); +} diff --git a/sys/platform/pc64/include/msi_machdep.h b/sys/platform/pc64/include/msi_machdep.h index 4c7967c5ea..53f8e8fea1 100644 --- a/sys/platform/pc64/include/msi_machdep.h +++ b/sys/platform/pc64/include/msi_machdep.h @@ -1,6 +1,9 @@ #ifndef _ARCH_MSI_MACHDEP_H_ #define _ARCH_MSI_MACHDEP_H_ +#include + #define MSI_X86_ADDR_BASE 0xfee00000 +#define MSI_X86_DATA_INTVEC IOART_INTVEC /* Interrupt vector. */ #endif /* !_ARCH_MSI_MACHDEP_H_ */ diff --git a/sys/platform/pc64/x86_64/msi.c b/sys/platform/pc64/x86_64/msi.c index e2ce7b956e..53196428e4 100644 --- a/sys/platform/pc64/x86_64/msi.c +++ b/sys/platform/pc64/x86_64/msi.c @@ -31,7 +31,6 @@ # define MSI_X86_DATA_DELNMI IOART_DELNMI # define MSI_X86_DATA_DELINIT IOART_DELINIT # define MSI_X86_DATA_DELEXINT IOART_DELEXINT -#define MSI_X86_DATA_INTVEC IOART_INTVEC /* Interrupt vector. */ #define MSI_X86_ADDR(lapic_id) \ (MSI_X86_ADDR_BASE | (lapic_id) << 12 | \ diff --git a/sys/sys/systimer.h b/sys/sys/systimer.h index 58c86e8a5b..e1f1c66b44 100644 --- a/sys/sys/systimer.h +++ b/sys/sys/systimer.h @@ -204,11 +204,13 @@ struct cputimer_intr { #define CPUTIMER_INTR_8254 0 #define CPUTIMER_INTR_LAPIC 1 #define CPUTIMER_INTR_VKERNEL 2 +#define CPUTIMER_INTR_VMM 3 /* NOTE: Keep the new values less than CPUTIMER_INTR_PRIO_MAX */ #define CPUTIMER_INTR_PRIO_8254 0 #define CPUTIMER_INTR_PRIO_LAPIC 10 #define CPUTIMER_INTR_PRIO_VKERNEL 20 +#define CPUTIMER_INTR_PRIO_VMM 500 #define CPUTIMER_INTR_PRIO_MAX 1000 #define CPUTIMER_INTR_CAP_NONE 0 -- 2.11.4.GIT