hyperv/vmbus: Fix interrupt timer detection logic.
[dragonfly.git] / sys / dev / virtual / hyperv / vmbus / vmbus.c
bloba01d967f0c3cc00a948c173dc159576ef01b893d
1 /*-
2 * Copyright (c) 2009-2012,2016 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
12 * disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/rman.h>
36 #include <sys/systimer.h>
37 #include <sys/thread.h>
38 #include <sys/thread2.h>
40 #include <machine/intr_machdep.h>
41 #include <machine/smp.h>
43 #include <dev/virtual/hyperv/hyperv_busdma.h>
44 #include <dev/virtual/hyperv/vmbus/hyperv_machdep.h>
45 #include <dev/virtual/hyperv/vmbus/hyperv_reg.h>
46 #include <dev/virtual/hyperv/vmbus/hyperv_var.h>
47 #include <dev/virtual/hyperv/vmbus/vmbus_reg.h>
48 #include <dev/virtual/hyperv/vmbus/vmbus_var.h>
50 #include "acpi.h"
51 #include "acpi_if.h"
52 #include "pcib_if.h"
54 #define MSR_HV_STIMER0_CFG_SINT \
55 ((((uint64_t)VMBUS_SINT_TIMER) << MSR_HV_STIMER_CFG_SINT_SHIFT) & \
56 MSR_HV_STIMER_CFG_SINT_MASK)
59 * Additionally required feature:
60 * - SynIC is needed for interrupt generation.
62 #define CPUID_HV_TIMER_MASK (CPUID_HV_MSR_SYNIC | \
63 CPUID_HV_MSR_SYNTIMER)
66 * NOTE: DO NOT CHANGE THIS.
68 #define VMBUS_SINT_MESSAGE 2
70 * NOTE:
71 * - DO NOT set it to the same value as VMBUS_SINT_MESSAGE.
72 * - DO NOT set it to 0.
74 #define VMBUS_SINT_TIMER 4
77 * NOTE: DO NOT CHANGE THESE
79 #define VMBUS_CONNID_MESSAGE 1
80 #define VMBUS_CONNID_EVENT 2
82 struct vmbus_msghc {
83 struct hypercall_postmsg_in *mh_inprm;
84 struct hypercall_postmsg_in mh_inprm_save;
85 struct hyperv_dma mh_inprm_dma;
87 struct vmbus_message *mh_resp;
88 struct vmbus_message mh_resp0;
91 struct vmbus_msghc_ctx {
92 struct vmbus_msghc *mhc_free;
93 struct lwkt_token mhc_free_token;
94 uint32_t mhc_flags;
96 struct vmbus_msghc *mhc_active;
97 struct lwkt_token mhc_active_token;
100 #define VMBUS_MSGHC_CTXF_DESTROY 0x0001
102 static int vmbus_probe(device_t);
103 static int vmbus_attach(device_t);
104 static int vmbus_detach(device_t);
105 static void vmbus_intr(void *);
106 static void vmbus_timer_intr_reload(struct cputimer_intr *,
107 sysclock_t);
108 static void vmbus_timer_intr_pcpuhand(
109 struct cputimer_intr *);
110 static void vmbus_timer_intr_restart(
111 struct cputimer_intr *);
113 static int vmbus_dma_alloc(struct vmbus_softc *);
114 static void vmbus_dma_free(struct vmbus_softc *);
115 static int vmbus_intr_setup(struct vmbus_softc *);
116 static void vmbus_intr_teardown(struct vmbus_softc *);
117 static void vmbus_synic_setup(void *);
118 static void vmbus_synic_teardown(void *);
119 static void vmbus_timer_stop(void *);
120 static void vmbus_timer_config(void *);
121 static int vmbus_init(struct vmbus_softc *);
122 static int vmbus_init_contact(struct vmbus_softc *,
123 uint32_t);
124 static void vmbus_timer_restart(void *);
125 static void vmbus_timer_msgintr(struct vmbus_pcpu_data *);
127 static void vmbus_chan_msgproc(struct vmbus_softc *,
128 const struct vmbus_message *);
130 static struct vmbus_msghc_ctx *vmbus_msghc_ctx_create(bus_dma_tag_t);
131 static void vmbus_msghc_ctx_destroy(
132 struct vmbus_msghc_ctx *);
133 static void vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *);
134 static struct vmbus_msghc *vmbus_msghc_alloc(bus_dma_tag_t);
135 static void vmbus_msghc_free(struct vmbus_msghc *);
136 static struct vmbus_msghc *vmbus_msghc_get1(struct vmbus_msghc_ctx *,
137 uint32_t);
139 static device_method_t vmbus_methods[] = {
140 /* Device interface */
141 DEVMETHOD(device_probe, vmbus_probe),
142 DEVMETHOD(device_attach, vmbus_attach),
143 DEVMETHOD(device_detach, vmbus_detach),
144 DEVMETHOD(device_shutdown, bus_generic_shutdown),
145 DEVMETHOD(device_suspend, bus_generic_suspend),
146 DEVMETHOD(device_resume, bus_generic_resume),
148 DEVMETHOD_END
151 static driver_t vmbus_driver = {
152 "vmbus",
153 vmbus_methods,
154 sizeof(struct vmbus_softc)
157 static devclass_t vmbus_devclass;
159 DRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL);
160 MODULE_DEPEND(vmbus, acpi, 1, 1, 1);
161 MODULE_VERSION(vmbus, 1);
163 static struct cputimer_intr vmbus_cputimer_intr = {
164 .freq = HYPERV_TIMER_FREQ,
165 .reload = vmbus_timer_intr_reload,
166 .enable = cputimer_intr_default_enable,
167 .config = cputimer_intr_default_config,
168 .restart = vmbus_timer_intr_restart,
169 .pmfixup = cputimer_intr_default_pmfixup,
170 .initclock = cputimer_intr_default_initclock,
171 .pcpuhand = vmbus_timer_intr_pcpuhand,
172 .next = SLIST_ENTRY_INITIALIZER,
173 .name = "hyperv",
174 .type = CPUTIMER_INTR_VMM,
175 .prio = CPUTIMER_INTR_PRIO_VMM,
176 .caps = CPUTIMER_INTR_CAP_PS,
177 .priv = NULL
180 static const uint32_t vmbus_version[] = {
181 VMBUS_VERSION_WIN8_1,
182 VMBUS_VERSION_WIN8,
183 VMBUS_VERSION_WIN7,
184 VMBUS_VERSION_WS2008
187 static int vmbus_timer_intr_enable = 1;
188 TUNABLE_INT("hw.vmbus.timer_intr.enable", &vmbus_timer_intr_enable);
190 static int
191 vmbus_probe(device_t dev)
193 char *id[] = { "VMBUS", NULL };
195 if (ACPI_ID_PROBE(device_get_parent(dev), dev, id) == NULL ||
196 device_get_unit(dev) != 0 || vmm_guest != VMM_GUEST_HYPERV ||
197 (hyperv_features & CPUID_HV_MSR_SYNIC) == 0)
198 return (ENXIO);
200 device_set_desc(dev, "Hyper-V vmbus");
202 return (0);
205 static int
206 vmbus_attach(device_t dev)
208 struct vmbus_softc *sc = device_get_softc(dev);
209 int error, cpu, use_timer;
212 * Basic setup.
214 sc->vmbus_dev = dev;
215 for (cpu = 0; cpu < ncpus; ++cpu) {
216 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu);
218 psc->sc = sc;
219 psc->cpuid = cpu;
220 psc->timer_last = UINT64_MAX;
224 * Should we use interrupt timer?
226 use_timer = 0;
227 if (device_get_unit(dev) == 0 &&
228 (hyperv_features & CPUID_HV_TIMER_MASK) == CPUID_HV_TIMER_MASK &&
229 hyperv_tc64 != NULL)
230 use_timer = 1;
233 * Create context for "post message" Hypercalls
235 sc->vmbus_msg_hc = vmbus_msghc_ctx_create(
236 bus_get_dma_tag(sc->vmbus_dev));
237 if (sc->vmbus_msg_hc == NULL)
238 return ENXIO;
241 * Allocate DMA stuffs.
243 error = vmbus_dma_alloc(sc);
244 if (error)
245 goto failed;
248 * Setup interrupt.
250 error = vmbus_intr_setup(sc);
251 if (error)
252 goto failed;
254 if (use_timer) {
256 * Make sure that interrupt timer is stopped.
258 lwkt_cpusync_simple(smp_active_mask, vmbus_timer_stop, sc);
262 * Setup SynIC.
264 lwkt_cpusync_simple(smp_active_mask, vmbus_synic_setup, sc);
265 sc->vmbus_flags |= VMBUS_FLAG_SYNIC;
268 * Initialize vmbus.
270 error = vmbus_init(sc);
271 if (error)
272 goto failed;
274 if (use_timer) {
276 * Configure and register vmbus interrupt timer.
278 lwkt_cpusync_simple(smp_active_mask, vmbus_timer_config, sc);
279 vmbus_cputimer_intr.priv = sc;
280 cputimer_intr_register(&vmbus_cputimer_intr);
281 if (vmbus_timer_intr_enable)
282 cputimer_intr_select(&vmbus_cputimer_intr, 0);
285 return 0;
286 failed:
287 vmbus_detach(dev);
288 return error;
291 static int
292 vmbus_detach(device_t dev)
294 struct vmbus_softc *sc = device_get_softc(dev);
296 /* TODO: uninitialize vmbus. */
297 /* TODO: stop and deregister timer */
299 if (sc->vmbus_flags & VMBUS_FLAG_SYNIC)
300 lwkt_cpusync_simple(smp_active_mask, vmbus_synic_teardown, sc);
301 vmbus_intr_teardown(sc);
302 vmbus_dma_free(sc);
304 if (sc->vmbus_msg_hc != NULL) {
305 vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc);
306 sc->vmbus_msg_hc = NULL;
308 return (0);
311 static __inline void
312 vmbus_msg_reset(volatile struct vmbus_message *msg)
314 msg->msg_type = HYPERV_MSGTYPE_NONE;
316 * Make sure that the write to msg_type (i.e. set to
317 * HYPERV_MSGTYPE_NONE) happens before we read the
318 * msg_flags and send EOM to the hypervisor.
320 cpu_mfence();
321 if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
323 * Ask the hypervisor to rescan message queue,
324 * and deliver new message if any.
326 wrmsr(MSR_HV_EOM, 0);
330 static void
331 vmbus_intr(void *xpsc)
333 struct vmbus_pcpu_data *psc = xpsc;
334 volatile struct vmbus_message *msg;
336 msg = psc->message + VMBUS_SINT_MESSAGE;
337 while (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) {
338 if (msg->msg_type == HYPERV_MSGTYPE_CHANNEL) {
339 /* Channel message */
340 vmbus_chan_msgproc(psc->sc,
341 __DEVOLATILE(const struct vmbus_message *, msg));
343 vmbus_msg_reset(msg);
347 static __inline void
348 vmbus_timer_oneshot(struct vmbus_pcpu_data *psc, uint64_t current)
350 psc->timer_last = current;
351 wrmsr(MSR_HV_STIMER0_COUNT, current);
354 static void
355 vmbus_timer_intr_reload(struct cputimer_intr *cti, sysclock_t reload)
357 struct globaldata *gd = mycpu;
358 struct vmbus_softc *sc = cti->priv;
359 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, gd->gd_cpuid);
360 uint64_t current;
362 reload = (uint64_t)reload * cti->freq / sys_cputimer->freq;
363 current = hyperv_tc64() + reload;
365 if (gd->gd_timer_running) {
366 if (current < psc->timer_last)
367 vmbus_timer_oneshot(psc, current);
368 } else {
369 gd->gd_timer_running = 1;
370 vmbus_timer_oneshot(psc, current);
374 static void
375 vmbus_timer_intr_pcpuhand(struct cputimer_intr *cti)
377 struct vmbus_softc *sc = cti->priv;
378 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, mycpuid);
380 vmbus_timer_msgintr(psc);
383 static void
384 vmbus_timer_intr_restart(struct cputimer_intr *cti)
386 lwkt_send_ipiq_mask(smp_active_mask, vmbus_timer_restart, cti->priv);
389 static struct vmbus_msghc *
390 vmbus_msghc_alloc(bus_dma_tag_t parent_dtag)
392 struct vmbus_msghc *mh;
394 mh = kmalloc(sizeof(*mh), M_DEVBUF, M_WAITOK | M_ZERO);
396 mh->mh_inprm = hyperv_dmamem_alloc(parent_dtag,
397 HYPERCALL_POSTMSGIN_ALIGN, 0, HYPERCALL_POSTMSGIN_SIZE,
398 &mh->mh_inprm_dma, BUS_DMA_WAITOK);
399 if (mh->mh_inprm == NULL) {
400 kfree(mh, M_DEVBUF);
401 return NULL;
403 return mh;
406 static void
407 vmbus_msghc_free(struct vmbus_msghc *mh)
409 hyperv_dmamem_free(&mh->mh_inprm_dma, mh->mh_inprm);
410 kfree(mh, M_DEVBUF);
413 static void
414 vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *mhc)
416 KASSERT(mhc->mhc_active == NULL, ("still have active msg hypercall"));
417 KASSERT(mhc->mhc_free == NULL, ("still have hypercall msg"));
419 lwkt_token_uninit(&mhc->mhc_free_token);
420 lwkt_token_uninit(&mhc->mhc_active_token);
421 kfree(mhc, M_DEVBUF);
424 static struct vmbus_msghc_ctx *
425 vmbus_msghc_ctx_create(bus_dma_tag_t parent_dtag)
427 struct vmbus_msghc_ctx *mhc;
429 mhc = kmalloc(sizeof(*mhc), M_DEVBUF, M_WAITOK | M_ZERO);
430 lwkt_token_init(&mhc->mhc_free_token, "msghcf");
431 lwkt_token_init(&mhc->mhc_active_token, "msghca");
433 mhc->mhc_free = vmbus_msghc_alloc(parent_dtag);
434 if (mhc->mhc_free == NULL) {
435 vmbus_msghc_ctx_free(mhc);
436 return NULL;
438 return mhc;
441 static struct vmbus_msghc *
442 vmbus_msghc_get1(struct vmbus_msghc_ctx *mhc, uint32_t dtor_flag)
444 struct vmbus_msghc *mh;
446 lwkt_gettoken(&mhc->mhc_free_token);
448 while ((mhc->mhc_flags & dtor_flag) == 0 && mhc->mhc_free == NULL)
449 tsleep(&mhc->mhc_free, 0, "gmsghc", 0);
450 if (mhc->mhc_flags & dtor_flag) {
451 /* Being destroyed */
452 mh = NULL;
453 } else {
454 mh = mhc->mhc_free;
455 KASSERT(mh != NULL, ("no free hypercall msg"));
456 KASSERT(mh->mh_resp == NULL,
457 ("hypercall msg has pending response"));
458 mhc->mhc_free = NULL;
461 lwkt_reltoken(&mhc->mhc_free_token);
463 return mh;
466 struct vmbus_msghc *
467 vmbus_msghc_get(struct vmbus_softc *sc, size_t dsize)
469 struct hypercall_postmsg_in *inprm;
470 struct vmbus_msghc *mh;
472 if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX)
473 return NULL;
475 mh = vmbus_msghc_get1(sc->vmbus_msg_hc, VMBUS_MSGHC_CTXF_DESTROY);
476 if (mh == NULL)
477 return NULL;
479 inprm = mh->mh_inprm;
480 memset(inprm, 0, HYPERCALL_POSTMSGIN_SIZE);
481 inprm->hc_connid = VMBUS_CONNID_MESSAGE;
482 inprm->hc_msgtype = HYPERV_MSGTYPE_CHANNEL;
483 inprm->hc_dsize = dsize;
485 return mh;
488 void
489 vmbus_msghc_put(struct vmbus_softc *sc, struct vmbus_msghc *mh)
491 struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
493 KASSERT(mhc->mhc_active == NULL, ("msg hypercall is active"));
494 mh->mh_resp = NULL;
496 lwkt_gettoken(&mhc->mhc_free_token);
497 KASSERT(mhc->mhc_free == NULL, ("has free hypercall msg"));
498 mhc->mhc_free = mh;
499 lwkt_reltoken(&mhc->mhc_free_token);
500 wakeup(&mhc->mhc_free);
503 void *
504 vmbus_msghc_dataptr(struct vmbus_msghc *mh)
506 return mh->mh_inprm->hc_data;
509 static void
510 vmbus_msghc_ctx_destroy(struct vmbus_msghc_ctx *mhc)
512 struct vmbus_msghc *mh;
514 lwkt_gettoken(&mhc->mhc_free_token);
515 mhc->mhc_flags |= VMBUS_MSGHC_CTXF_DESTROY;
516 lwkt_reltoken(&mhc->mhc_free_token);
517 wakeup(&mhc->mhc_free);
519 mh = vmbus_msghc_get1(mhc, 0);
520 if (mh == NULL)
521 panic("can't get msghc");
523 vmbus_msghc_free(mh);
524 vmbus_msghc_ctx_free(mhc);
528 vmbus_msghc_exec_noresult(struct vmbus_msghc *mh)
530 int i, wait_ticks = 1;
533 * Save the input parameter so that we could restore the input
534 * parameter if the Hypercall failed.
536 * XXX
537 * Is this really necessary?! i.e. Will the Hypercall ever
538 * overwrite the input parameter?
540 memcpy(&mh->mh_inprm_save, mh->mh_inprm, HYPERCALL_POSTMSGIN_SIZE);
543 * In order to cope with transient failures, e.g. insufficient
544 * resources on host side, we retry the post message Hypercall
545 * several times. 20 retries seem sufficient.
547 #define HC_RETRY_MAX 20
549 for (i = 0; i < HC_RETRY_MAX; ++i) {
550 uint64_t status;
552 status = hypercall_post_message(mh->mh_inprm_dma.hv_paddr);
553 if (status == HYPERCALL_STATUS_SUCCESS)
554 return 0;
556 tsleep(&status, 0, "hcpmsg", wait_ticks);
557 if (wait_ticks < hz)
558 wait_ticks *= 2;
560 /* Restore input parameter and try again */
561 memcpy(mh->mh_inprm, &mh->mh_inprm_save,
562 HYPERCALL_POSTMSGIN_SIZE);
565 #undef HC_RETRY_MAX
567 return EIO;
571 vmbus_msghc_exec(struct vmbus_softc *sc, struct vmbus_msghc *mh)
573 struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
574 int error;
576 KASSERT(mh->mh_resp == NULL, ("hypercall msg has pending response"));
578 lwkt_gettoken(&mhc->mhc_active_token);
579 KASSERT(mhc->mhc_active == NULL, ("pending active msg hypercall"));
580 mhc->mhc_active = mh;
581 lwkt_reltoken(&mhc->mhc_active_token);
583 error = vmbus_msghc_exec_noresult(mh);
584 if (error) {
585 lwkt_gettoken(&mhc->mhc_active_token);
586 KASSERT(mhc->mhc_active == mh, ("msghc mismatch"));
587 mhc->mhc_active = NULL;
588 lwkt_reltoken(&mhc->mhc_active_token);
590 return error;
593 const struct vmbus_message *
594 vmbus_msghc_wait_result(struct vmbus_softc *sc, struct vmbus_msghc *mh)
596 struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
598 lwkt_gettoken(&mhc->mhc_active_token);
600 KASSERT(mhc->mhc_active == mh, ("msghc mismatch"));
601 while (mh->mh_resp == NULL)
602 tsleep(&mhc->mhc_active, 0, "wmsghc", 0);
603 mhc->mhc_active = NULL;
605 lwkt_reltoken(&mhc->mhc_active_token);
607 return mh->mh_resp;
610 void
611 vmbus_msghc_wakeup(struct vmbus_softc *sc, const struct vmbus_message *msg)
613 struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
614 struct vmbus_msghc *mh;
616 lwkt_gettoken(&mhc->mhc_active_token);
618 mh = mhc->mhc_active;
619 KASSERT(mh != NULL, ("no pending msg hypercall"));
620 memcpy(&mh->mh_resp0, msg, sizeof(mh->mh_resp0));
621 mh->mh_resp = &mh->mh_resp0;
623 lwkt_reltoken(&mhc->mhc_active_token);
624 wakeup(&mhc->mhc_active);
627 static int
628 vmbus_dma_alloc(struct vmbus_softc *sc)
630 bus_dma_tag_t parent_dtag;
631 uint8_t *evtflags;
632 int cpu;
634 parent_dtag = bus_get_dma_tag(sc->vmbus_dev);
635 for (cpu = 0; cpu < ncpus; ++cpu) {
636 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu);
639 * Per-cpu messages and event flags.
641 psc->message = hyperv_dmamem_alloc(parent_dtag,
642 PAGE_SIZE, 0, PAGE_SIZE, &psc->message_dma,
643 BUS_DMA_WAITOK | BUS_DMA_ZERO);
644 if (psc->message == NULL)
645 return ENOMEM;
647 psc->event_flags = hyperv_dmamem_alloc(parent_dtag,
648 PAGE_SIZE, 0, PAGE_SIZE, &psc->event_flags_dma,
649 BUS_DMA_WAITOK | BUS_DMA_ZERO);
650 if (psc->event_flags == NULL)
651 return ENOMEM;
654 evtflags = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
655 PAGE_SIZE, &sc->vmbus_evtflags_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
656 if (evtflags == NULL)
657 return ENOMEM;
658 sc->vmbus_rx_evtflags = (u_long *)evtflags;
659 sc->vmbus_tx_evtflags = (u_long *)(evtflags + (PAGE_SIZE / 2));
660 sc->vmbus_evtflags = evtflags;
662 sc->vmbus_mnf1 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
663 PAGE_SIZE, &sc->vmbus_mnf1_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
664 if (sc->vmbus_mnf1 == NULL)
665 return ENOMEM;
667 sc->vmbus_mnf2 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
668 PAGE_SIZE, &sc->vmbus_mnf2_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
669 if (sc->vmbus_mnf2 == NULL)
670 return ENOMEM;
672 return 0;
675 static void
676 vmbus_dma_free(struct vmbus_softc *sc)
678 int cpu;
680 if (sc->vmbus_evtflags != NULL) {
681 hyperv_dmamem_free(&sc->vmbus_evtflags_dma, sc->vmbus_evtflags);
682 sc->vmbus_evtflags = NULL;
683 sc->vmbus_rx_evtflags = NULL;
684 sc->vmbus_tx_evtflags = NULL;
686 if (sc->vmbus_mnf1 != NULL) {
687 hyperv_dmamem_free(&sc->vmbus_mnf1_dma, sc->vmbus_mnf1);
688 sc->vmbus_mnf1 = NULL;
690 if (sc->vmbus_mnf2 != NULL) {
691 hyperv_dmamem_free(&sc->vmbus_mnf2_dma, sc->vmbus_mnf2);
692 sc->vmbus_mnf2 = NULL;
695 for (cpu = 0; cpu < ncpus; ++cpu) {
696 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu);
698 if (psc->message != NULL) {
699 hyperv_dmamem_free(&psc->message_dma, psc->message);
700 psc->message = NULL;
702 if (psc->event_flags != NULL) {
703 hyperv_dmamem_free(&psc->event_flags_dma,
704 psc->event_flags);
705 psc->event_flags = NULL;
710 static int
711 vmbus_intr_setup(struct vmbus_softc *sc)
713 device_t dev = sc->vmbus_dev;
714 device_t bus = device_get_parent(device_get_parent(dev));
715 int rid, cpu;
717 rid = 0;
718 for (cpu = 0; cpu < ncpus; ++cpu) {
719 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu);
720 uint64_t msi_addr;
721 uint32_t msi_data;
722 int error;
724 error = PCIB_ALLOC_MSIX(bus, dev, &psc->intr_irq, cpu);
725 if (error) {
726 device_printf(dev, "alloc vector on cpu%d failed: %d\n",
727 cpu, error);
728 return ENXIO;
730 psc->intr_rid = ++rid;
732 psc->intr_res = BUS_ALLOC_RESOURCE(bus, dev, SYS_RES_IRQ,
733 &psc->intr_rid, psc->intr_irq, psc->intr_irq, 1,
734 RF_ACTIVE, cpu);
735 if (psc->intr_res == NULL) {
736 device_printf(dev, "alloc irq on cpu%d failed: %d\n",
737 cpu, error);
738 return ENXIO;
741 error = PCIB_MAP_MSI(bus, dev, rman_get_start(psc->intr_res),
742 &msi_addr, &msi_data, cpu);
743 if (error) {
744 device_printf(dev, "map irq on cpu%d failed: %d\n",
745 cpu, error);
746 return ENXIO;
748 psc->intr_vec = hyperv_msi2vector(msi_addr, msi_data);
750 if (bootverbose) {
751 device_printf(dev, "vector %d irq %d on cpu%d\n",
752 psc->intr_vec, psc->intr_irq, cpu);
755 ksnprintf(psc->intr_desc, sizeof(psc->intr_desc), "%s cpu%d",
756 device_get_nameunit(dev), cpu);
757 error = bus_setup_intr_descr(dev, psc->intr_res, INTR_MPSAFE,
758 vmbus_intr, psc, &psc->intr_hand, NULL, psc->intr_desc);
759 if (error) {
760 device_printf(dev, "setup intr on cpu%d failed: %d\n",
761 cpu, error);
762 return ENXIO;
765 return 0;
768 static void
769 vmbus_intr_teardown(struct vmbus_softc *sc)
771 device_t dev = sc->vmbus_dev;
772 device_t bus = device_get_parent(device_get_parent(dev));
773 int cpu;
775 for (cpu = 0; cpu < ncpus; ++cpu) {
776 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu);
778 if (psc->intr_hand != NULL) {
779 bus_teardown_intr(dev, psc->intr_res, psc->intr_hand);
780 psc->intr_hand = NULL;
783 if (psc->intr_res != NULL) {
784 BUS_RELEASE_RESOURCE(bus, dev, SYS_RES_IRQ,
785 psc->intr_rid, psc->intr_res);
786 psc->intr_res = NULL;
789 if (psc->intr_rid != 0) {
790 PCIB_RELEASE_MSIX(bus, dev, psc->intr_irq, psc->cpuid);
791 psc->intr_rid = 0;
796 static void
797 vmbus_synic_setup(void *xsc)
799 struct vmbus_softc *sc = xsc;
800 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, mycpuid);
801 uint64_t val, orig;
802 uint32_t sint;
804 if (hyperv_features & CPUID_HV_MSR_VP_INDEX) {
806 * Save virtual processor id.
808 psc->vcpuid = rdmsr(MSR_HV_VP_INDEX);
809 } else {
811 * XXX
812 * Virtual processoor id is only used by a pretty broken
813 * channel selection code from storvsc. It's nothing
814 * critical even if CPUID_HV_MSR_VP_INDEX is not set; keep
815 * moving on.
817 psc->vcpuid = mycpuid;
821 * Setup the SynIC message.
823 orig = rdmsr(MSR_HV_SIMP);
824 val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) |
825 ((psc->message_dma.hv_paddr >> PAGE_SHIFT) << MSR_HV_SIMP_PGSHIFT);
826 wrmsr(MSR_HV_SIMP, val);
829 * Setup the SynIC event flags.
831 orig = rdmsr(MSR_HV_SIEFP);
832 val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) |
833 ((psc->event_flags_dma.hv_paddr >> PAGE_SHIFT) <<
834 MSR_HV_SIEFP_PGSHIFT);
835 wrmsr(MSR_HV_SIEFP, val);
839 * Configure and unmask SINT for message and event flags.
841 sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
842 orig = rdmsr(sint);
843 val = psc->intr_vec | /* MSR_HV_SINT_AUTOEOI | notyet */
844 (orig & MSR_HV_SINT_RSVD_MASK);
845 wrmsr(sint, val);
848 * Configure and unmask SINT for timer.
850 sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
851 orig = rdmsr(sint);
852 val = XTIMER_OFFSET | /* MSR_HV_SINT_AUTOEOI | notyet */
853 (orig & MSR_HV_SINT_RSVD_MASK);
854 wrmsr(sint, val);
857 * All done; enable SynIC.
859 orig = rdmsr(MSR_HV_SCONTROL);
860 val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK);
861 wrmsr(MSR_HV_SCONTROL, val);
864 static void
865 vmbus_timer_stop(void *arg __unused)
867 for (;;) {
868 uint64_t val;
870 /* Stop counting, and this also implies disabling STIMER0 */
871 wrmsr(MSR_HV_STIMER0_COUNT, 0);
873 val = rdmsr(MSR_HV_STIMER0_CONFIG);
874 if ((val & MSR_HV_STIMER_CFG_ENABLE) == 0)
875 break;
876 cpu_pause();
880 static void
881 vmbus_timer_config(void *arg __unused)
884 * Make sure that STIMER0 is really disabled before writing
885 * to STIMER0_CONFIG.
887 * "Writing to the configuration register of a timer that
888 * is already enabled may result in undefined behaviour."
890 vmbus_timer_stop(arg);
891 wrmsr(MSR_HV_STIMER0_CONFIG,
892 MSR_HV_STIMER_CFG_AUTOEN | MSR_HV_STIMER0_CFG_SINT);
895 static void
896 vmbus_timer_msgintr(struct vmbus_pcpu_data *psc)
898 volatile struct vmbus_message *msg;
900 msg = psc->message + VMBUS_SINT_TIMER;
901 if (msg->msg_type == HYPERV_MSGTYPE_TIMER_EXPIRED)
902 vmbus_msg_reset(msg);
905 static void
906 vmbus_timer_restart(void *xsc)
908 struct vmbus_softc *sc = xsc;
909 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, mycpuid);
911 crit_enter();
912 vmbus_timer_msgintr(psc);
913 vmbus_timer_oneshot(psc, hyperv_tc64() + 1);
914 crit_exit();
917 static void
918 vmbus_synic_teardown(void *arg __unused)
920 uint64_t orig;
921 uint32_t sint;
924 * Disable SynIC.
926 orig = rdmsr(MSR_HV_SCONTROL);
927 wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK));
930 * Mask message and event flags SINT.
932 sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
933 orig = rdmsr(sint);
934 wrmsr(sint, orig | MSR_HV_SINT_MASKED);
937 * Mask timer SINT.
939 sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
940 orig = rdmsr(sint);
941 wrmsr(sint, orig | MSR_HV_SINT_MASKED);
944 * Teardown SynIC message.
946 orig = rdmsr(MSR_HV_SIMP);
947 wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK));
950 * Teardown SynIC event flags.
952 orig = rdmsr(MSR_HV_SIEFP);
953 wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK));
956 static int
957 vmbus_init_contact(struct vmbus_softc *sc, uint32_t version)
959 struct vmbus_chanmsg_init_contact *req;
960 const struct vmbus_chanmsg_version_resp *resp;
961 const struct vmbus_message *msg;
962 struct vmbus_msghc *mh;
963 int error, supp = 0;
965 mh = vmbus_msghc_get(sc, sizeof(*req));
966 if (mh == NULL)
967 return ENXIO;
969 req = vmbus_msghc_dataptr(mh);
970 req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_INIT_CONTACT;
971 req->chm_ver = version;
972 req->chm_evtflags = sc->vmbus_evtflags_dma.hv_paddr;
973 req->chm_mnf1 = sc->vmbus_mnf1_dma.hv_paddr;
974 req->chm_mnf2 = sc->vmbus_mnf2_dma.hv_paddr;
976 error = vmbus_msghc_exec(sc, mh);
977 if (error) {
978 vmbus_msghc_put(sc, mh);
979 return error;
982 msg = vmbus_msghc_wait_result(sc, mh);
983 resp = (const struct vmbus_chanmsg_version_resp *)msg->msg_data;
984 supp = resp->chm_supp;
986 vmbus_msghc_put(sc, mh);
988 return (supp ? 0 : EOPNOTSUPP);
991 static int
992 vmbus_init(struct vmbus_softc *sc)
994 int i;
996 for (i = 0; i < nitems(vmbus_version); ++i) {
997 int error;
999 error = vmbus_init_contact(sc, vmbus_version[i]);
1000 if (!error) {
1001 sc->vmbus_version = vmbus_version[i];
1002 device_printf(sc->vmbus_dev, "version %u.%u\n",
1003 (sc->vmbus_version >> 16),
1004 (sc->vmbus_version & 0xffff));
1005 return 0;
1008 return ENXIO;
1011 static void
1012 vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
1014 const struct vmbus_chanmsg_hdr *hdr;
1016 hdr = (const struct vmbus_chanmsg_hdr *)msg->msg_data;
1018 /* TODO */
1019 if (hdr->chm_type == VMBUS_CHANMSG_TYPE_VERSION_RESP)
1020 vmbus_msghc_wakeup(sc, msg);