hyperv/vmbus: Complete vmbus initialization; interrupt cputimer is enabled
[dragonfly.git] / sys / dev / virtual / hyperv / vmbus / vmbus.c
blob22e6b36cb2d9c230d890566ae7d465b4ec7bc716
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/include/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 * NOTE: DO NOT CHANGE THIS.
61 #define VMBUS_SINT_MESSAGE 2
63 * NOTE:
64 * - DO NOT set it to the same value as VMBUS_SINT_MESSAGE.
65 * - DO NOT set it to 0.
67 #define VMBUS_SINT_TIMER 4
70 * NOTE: DO NOT CHANGE THESE
72 #define VMBUS_CONNID_MESSAGE 1
73 #define VMBUS_CONNID_EVENT 2
75 struct vmbus_msghc {
76 struct hypercall_postmsg_in *mh_inprm;
77 struct hypercall_postmsg_in mh_inprm_save;
78 struct hyperv_dma mh_inprm_dma;
80 struct vmbus_message *mh_resp;
81 struct vmbus_message mh_resp0;
84 struct vmbus_msghc_ctx {
85 struct vmbus_msghc *mhc_free;
86 struct lwkt_token mhc_free_token;
87 uint32_t mhc_flags;
89 struct vmbus_msghc *mhc_active;
90 struct lwkt_token mhc_active_token;
93 #define VMBUS_MSGHC_CTXF_DESTROY 0x0001
95 static int vmbus_probe(device_t);
96 static int vmbus_attach(device_t);
97 static int vmbus_detach(device_t);
98 static void vmbus_intr(void *);
99 static void vmbus_timer_intr_reload(struct cputimer_intr *,
100 sysclock_t);
101 static void vmbus_timer_intr_pcpuhand(
102 struct cputimer_intr *);
103 static void vmbus_timer_intr_restart(
104 struct cputimer_intr *);
106 static int vmbus_dma_alloc(struct vmbus_softc *);
107 static void vmbus_dma_free(struct vmbus_softc *);
108 static int vmbus_intr_setup(struct vmbus_softc *);
109 static void vmbus_intr_teardown(struct vmbus_softc *);
110 static int vmbus_intr_rid(struct resource_list *, int);
111 static void vmbus_synic_setup(void *);
112 static void vmbus_synic_teardown(void *);
113 static void vmbus_timer_stop(void *);
114 static void vmbus_timer_config(void *);
115 static int vmbus_init(struct vmbus_softc *);
116 static int vmbus_init_contact(struct vmbus_softc *,
117 uint32_t);
118 static void vmbus_timer_restart(void *);
119 static void vmbus_timer_msgintr(struct vmbus_pcpu_data *);
121 static void vmbus_chan_msgproc(struct vmbus_softc *,
122 const struct vmbus_message *);
124 static struct vmbus_msghc_ctx *vmbus_msghc_ctx_create(bus_dma_tag_t);
125 static void vmbus_msghc_ctx_destroy(
126 struct vmbus_msghc_ctx *);
127 static void vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *);
128 static struct vmbus_msghc *vmbus_msghc_alloc(bus_dma_tag_t);
129 static void vmbus_msghc_free(struct vmbus_msghc *);
130 static struct vmbus_msghc *vmbus_msghc_get1(struct vmbus_msghc_ctx *,
131 uint32_t);
133 static device_method_t vmbus_methods[] = {
134 /* Device interface */
135 DEVMETHOD(device_probe, vmbus_probe),
136 DEVMETHOD(device_attach, vmbus_attach),
137 DEVMETHOD(device_detach, vmbus_detach),
138 DEVMETHOD(device_shutdown, bus_generic_shutdown),
139 DEVMETHOD(device_suspend, bus_generic_suspend),
140 DEVMETHOD(device_resume, bus_generic_resume),
142 DEVMETHOD_END
145 static driver_t vmbus_driver = {
146 "vmbus",
147 vmbus_methods,
148 sizeof(struct vmbus_softc)
151 static devclass_t vmbus_devclass;
153 DRIVER_MODULE(vmbus, acpi, vmbus_driver, vmbus_devclass, NULL, NULL);
154 MODULE_DEPEND(vmbus, acpi, 1, 1, 1);
155 MODULE_VERSION(vmbus, 1);
157 static struct cputimer_intr vmbus_cputimer_intr = {
158 .freq = HYPERV_TIMER_FREQ,
159 .reload = vmbus_timer_intr_reload,
160 .enable = cputimer_intr_default_enable,
161 .config = cputimer_intr_default_config,
162 .restart = vmbus_timer_intr_restart,
163 .pmfixup = cputimer_intr_default_pmfixup,
164 .initclock = cputimer_intr_default_initclock,
165 .pcpuhand = vmbus_timer_intr_pcpuhand,
166 .next = SLIST_ENTRY_INITIALIZER,
167 .name = "hyperv",
168 .type = CPUTIMER_INTR_VMM,
169 .prio = CPUTIMER_INTR_PRIO_VMM,
170 .caps = CPUTIMER_INTR_CAP_PS,
171 .priv = NULL
174 static const uint32_t vmbus_version[] = {
175 VMBUS_VERSION_WIN8_1,
176 VMBUS_VERSION_WIN8,
177 VMBUS_VERSION_WIN7,
178 VMBUS_VERSION_WS2008
181 static int vmbus_timer_intr_enable = 1;
182 TUNABLE_INT("hw.vmbus.timer_intr.enable", &vmbus_timer_intr_enable);
184 static int
185 vmbus_probe(device_t dev)
187 char *id[] = { "VMBUS", NULL };
189 if (ACPI_ID_PROBE(device_get_parent(dev), dev, id) == NULL ||
190 device_get_unit(dev) != 0 || vmm_guest != VMM_GUEST_HYPERV ||
191 (hyperv_features & CPUID_HV_MSR_SYNIC) == 0)
192 return (ENXIO);
194 device_set_desc(dev, "Hyper-V vmbus");
196 return (0);
199 static int
200 vmbus_attach(device_t dev)
202 struct vmbus_softc *sc = device_get_softc(dev);
203 int error, cpu, unit;
205 sc->vmbus_dev = dev;
206 for (cpu = 0; cpu < ncpus; ++cpu) {
207 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu);
209 psc->sc = sc;
210 psc->cpuid = cpu;
211 psc->timer_last = UINT64_MAX;
213 unit = device_get_unit(dev);
216 * Create context for "post message" Hypercalls
218 sc->vmbus_msg_hc = vmbus_msghc_ctx_create(
219 bus_get_dma_tag(sc->vmbus_dev));
220 if (sc->vmbus_msg_hc == NULL)
221 return ENXIO;
224 * Allocate DMA stuffs.
226 error = vmbus_dma_alloc(sc);
227 if (error)
228 goto failed;
231 * Setup interrupt.
233 error = vmbus_intr_setup(sc);
234 if (error)
235 goto failed;
237 if (unit == 0) {
239 * Make sure timer is stopped.
241 lwkt_cpusync_simple(smp_active_mask, vmbus_timer_stop, sc);
245 * Setup SynIC.
247 lwkt_cpusync_simple(smp_active_mask, vmbus_synic_setup, sc);
248 sc->vmbus_flags |= VMBUS_FLAG_SYNIC;
251 * Initialize vmbus.
253 error = vmbus_init(sc);
254 if (error)
255 goto failed;
257 if (unit == 0) {
259 * Configure and register vmbus interrupt cputimer.
261 lwkt_cpusync_simple(smp_active_mask, vmbus_timer_config, sc);
262 vmbus_cputimer_intr.priv = sc;
263 cputimer_intr_register(&vmbus_cputimer_intr);
264 if (vmbus_timer_intr_enable)
265 cputimer_intr_select(&vmbus_cputimer_intr, 0);
268 return 0;
269 failed:
270 vmbus_detach(dev);
271 return error;
274 static int
275 vmbus_detach(device_t dev)
277 struct vmbus_softc *sc = device_get_softc(dev);
279 /* TODO: uninitialize vmbus. */
280 /* TODO: stop and deregister timer */
282 if (sc->vmbus_flags & VMBUS_FLAG_SYNIC)
283 lwkt_cpusync_simple(smp_active_mask, vmbus_synic_teardown, sc);
284 vmbus_intr_teardown(sc);
285 vmbus_dma_free(sc);
287 if (sc->vmbus_msg_hc != NULL) {
288 vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc);
289 sc->vmbus_msg_hc = NULL;
291 return (0);
294 static void
295 vmbus_intr(void *xpsc)
297 struct vmbus_pcpu_data *psc = xpsc;
298 volatile struct vmbus_message *msg;
300 msg = psc->message + VMBUS_SINT_MESSAGE;
301 while (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) {
302 if (msg->msg_type == HYPERV_MSGTYPE_CHANNEL) {
303 /* Channel message */
304 vmbus_chan_msgproc(psc->sc,
305 __DEVOLATILE(const struct vmbus_message *, msg));
308 msg->msg_type = HYPERV_MSGTYPE_NONE;
310 * Make sure the write to msg_type (i.e. set to
311 * HYPERV_MSGTYPE_NONE) happens before we read the
312 * msg_flags and EOMing. Otherwise, the EOMing will
313 * not deliver any more messages since there is no
314 * empty slot
316 cpu_mfence();
317 if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
319 * This will cause message queue rescan to possibly
320 * deliver another msg from the hypervisor
322 wrmsr(MSR_HV_EOM, 0);
327 static __inline void
328 vmbus_timer_oneshot(struct vmbus_pcpu_data *psc, uint64_t current)
330 psc->timer_last = current;
331 wrmsr(MSR_HV_STIMER0_COUNT, current);
334 static void
335 vmbus_timer_intr_reload(struct cputimer_intr *cti, sysclock_t reload)
337 struct globaldata *gd = mycpu;
338 struct vmbus_softc *sc = cti->priv;
339 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, gd->gd_cpuid);
340 uint64_t current;
342 reload = (uint64_t)reload * cti->freq / sys_cputimer->freq;
343 current = rdmsr(MSR_HV_TIME_REF_COUNT) + reload;
345 if (gd->gd_timer_running) {
346 if (current < psc->timer_last)
347 vmbus_timer_oneshot(psc, current);
348 } else {
349 gd->gd_timer_running = 1;
350 vmbus_timer_oneshot(psc, current);
354 static void
355 vmbus_timer_intr_pcpuhand(struct cputimer_intr *cti)
357 struct vmbus_softc *sc = cti->priv;
358 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, mycpuid);
360 vmbus_timer_msgintr(psc);
363 static void
364 vmbus_timer_intr_restart(struct cputimer_intr *cti)
366 lwkt_send_ipiq_mask(smp_active_mask, vmbus_timer_restart, cti->priv);
369 static struct vmbus_msghc *
370 vmbus_msghc_alloc(bus_dma_tag_t parent_dtag)
372 struct vmbus_msghc *mh;
374 mh = kmalloc(sizeof(*mh), M_DEVBUF, M_WAITOK | M_ZERO);
376 mh->mh_inprm = hyperv_dmamem_alloc(parent_dtag,
377 HYPERCALL_POSTMSGIN_ALIGN, 0, HYPERCALL_POSTMSGIN_SIZE,
378 &mh->mh_inprm_dma, BUS_DMA_WAITOK);
379 if (mh->mh_inprm == NULL) {
380 kfree(mh, M_DEVBUF);
381 return NULL;
383 return mh;
386 static void
387 vmbus_msghc_free(struct vmbus_msghc *mh)
389 hyperv_dmamem_free(&mh->mh_inprm_dma, mh->mh_inprm);
390 kfree(mh, M_DEVBUF);
393 static void
394 vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *mhc)
396 KASSERT(mhc->mhc_active == NULL, ("still have active msg hypercall"));
397 KASSERT(mhc->mhc_free == NULL, ("still have hypercall msg"));
399 lwkt_token_uninit(&mhc->mhc_free_token);
400 lwkt_token_uninit(&mhc->mhc_active_token);
401 kfree(mhc, M_DEVBUF);
404 static struct vmbus_msghc_ctx *
405 vmbus_msghc_ctx_create(bus_dma_tag_t parent_dtag)
407 struct vmbus_msghc_ctx *mhc;
409 mhc = kmalloc(sizeof(*mhc), M_DEVBUF, M_WAITOK | M_ZERO);
410 lwkt_token_init(&mhc->mhc_free_token, "msghcf");
411 lwkt_token_init(&mhc->mhc_active_token, "msghca");
413 mhc->mhc_free = vmbus_msghc_alloc(parent_dtag);
414 if (mhc->mhc_free == NULL) {
415 vmbus_msghc_ctx_free(mhc);
416 return NULL;
418 return mhc;
421 static struct vmbus_msghc *
422 vmbus_msghc_get1(struct vmbus_msghc_ctx *mhc, uint32_t dtor_flag)
424 struct vmbus_msghc *mh;
426 lwkt_gettoken(&mhc->mhc_free_token);
428 while ((mhc->mhc_flags & dtor_flag) == 0 && mhc->mhc_free == NULL)
429 tsleep(&mhc->mhc_free, 0, "gmsghc", 0);
430 if (mhc->mhc_flags & dtor_flag) {
431 /* Being destroyed */
432 mh = NULL;
433 } else {
434 mh = mhc->mhc_free;
435 KASSERT(mh != NULL, ("no free hypercall msg"));
436 KASSERT(mh->mh_resp == NULL,
437 ("hypercall msg has pending response"));
438 mhc->mhc_free = NULL;
441 lwkt_reltoken(&mhc->mhc_free_token);
443 return mh;
446 struct vmbus_msghc *
447 vmbus_msghc_get(struct vmbus_softc *sc, size_t dsize)
449 struct hypercall_postmsg_in *inprm;
450 struct vmbus_msghc *mh;
452 if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX)
453 return NULL;
455 mh = vmbus_msghc_get1(sc->vmbus_msg_hc, VMBUS_MSGHC_CTXF_DESTROY);
456 if (mh == NULL)
457 return NULL;
459 inprm = mh->mh_inprm;
460 memset(inprm, 0, HYPERCALL_POSTMSGIN_SIZE);
461 inprm->hc_connid = VMBUS_CONNID_MESSAGE;
462 inprm->hc_msgtype = HYPERV_MSGTYPE_CHANNEL;
463 inprm->hc_dsize = dsize;
465 return mh;
468 void
469 vmbus_msghc_put(struct vmbus_softc *sc, struct vmbus_msghc *mh)
471 struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
473 KASSERT(mhc->mhc_active == NULL, ("msg hypercall is active"));
474 mh->mh_resp = NULL;
476 lwkt_gettoken(&mhc->mhc_free_token);
477 KASSERT(mhc->mhc_free == NULL, ("has free hypercall msg"));
478 mhc->mhc_free = mh;
479 lwkt_reltoken(&mhc->mhc_free_token);
480 wakeup(&mhc->mhc_free);
483 void *
484 vmbus_msghc_dataptr(struct vmbus_msghc *mh)
486 return mh->mh_inprm->hc_data;
489 static void
490 vmbus_msghc_ctx_destroy(struct vmbus_msghc_ctx *mhc)
492 struct vmbus_msghc *mh;
494 lwkt_gettoken(&mhc->mhc_free_token);
495 mhc->mhc_flags |= VMBUS_MSGHC_CTXF_DESTROY;
496 lwkt_reltoken(&mhc->mhc_free_token);
497 wakeup(&mhc->mhc_free);
499 mh = vmbus_msghc_get1(mhc, 0);
500 if (mh == NULL)
501 panic("can't get msghc");
503 vmbus_msghc_free(mh);
504 vmbus_msghc_ctx_free(mhc);
508 vmbus_msghc_exec_noresult(struct vmbus_msghc *mh)
510 int i, wait_ticks = 1;
513 * Save the input parameter so that we could restore the input
514 * parameter if the Hypercall failed.
516 * XXX
517 * Is this really necessary?! i.e. Will the Hypercall ever
518 * overwrite the input parameter?
520 memcpy(&mh->mh_inprm_save, mh->mh_inprm, HYPERCALL_POSTMSGIN_SIZE);
523 * In order to cope with transient failures, e.g. insufficient
524 * resources on host side, we retry the post message Hypercall
525 * several times. 20 retries seem sufficient.
527 #define HC_RETRY_MAX 20
529 for (i = 0; i < HC_RETRY_MAX; ++i) {
530 uint64_t status;
532 status = hypercall_post_message(mh->mh_inprm_dma.hv_paddr);
533 if (status == HYPERCALL_STATUS_SUCCESS)
534 return 0;
536 tsleep(&status, 0, "hcpmsg", wait_ticks);
537 if (wait_ticks < hz)
538 wait_ticks *= 2;
540 /* Restore input parameter and try again */
541 memcpy(mh->mh_inprm, &mh->mh_inprm_save,
542 HYPERCALL_POSTMSGIN_SIZE);
545 #undef HC_RETRY_MAX
547 return EIO;
551 vmbus_msghc_exec(struct vmbus_softc *sc, struct vmbus_msghc *mh)
553 struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
554 int error;
556 KASSERT(mh->mh_resp == NULL, ("hypercall msg has pending response"));
558 lwkt_gettoken(&mhc->mhc_active_token);
559 KASSERT(mhc->mhc_active == NULL, ("pending active msg hypercall"));
560 mhc->mhc_active = mh;
561 lwkt_reltoken(&mhc->mhc_active_token);
563 error = vmbus_msghc_exec_noresult(mh);
564 if (error) {
565 lwkt_gettoken(&mhc->mhc_active_token);
566 KASSERT(mhc->mhc_active == mh, ("msghc mismatch"));
567 mhc->mhc_active = NULL;
568 lwkt_reltoken(&mhc->mhc_active_token);
570 return error;
573 const struct vmbus_message *
574 vmbus_msghc_wait_result(struct vmbus_softc *sc, struct vmbus_msghc *mh)
576 struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
578 lwkt_gettoken(&mhc->mhc_active_token);
580 KASSERT(mhc->mhc_active == mh, ("msghc mismatch"));
581 while (mh->mh_resp == NULL)
582 tsleep(&mhc->mhc_active, 0, "wmsghc", 0);
583 mhc->mhc_active = NULL;
585 lwkt_reltoken(&mhc->mhc_active_token);
587 return mh->mh_resp;
590 void
591 vmbus_msghc_wakeup(struct vmbus_softc *sc, const struct vmbus_message *msg)
593 struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
594 struct vmbus_msghc *mh;
596 lwkt_gettoken(&mhc->mhc_active_token);
598 mh = mhc->mhc_active;
599 KASSERT(mh != NULL, ("no pending msg hypercall"));
600 memcpy(&mh->mh_resp0, msg, sizeof(mh->mh_resp0));
601 mh->mh_resp = &mh->mh_resp0;
603 lwkt_reltoken(&mhc->mhc_active_token);
604 wakeup(&mhc->mhc_active);
607 static int
608 vmbus_dma_alloc(struct vmbus_softc *sc)
610 bus_dma_tag_t parent_dtag;
611 uint8_t *evtflags;
612 int cpu;
614 parent_dtag = bus_get_dma_tag(sc->vmbus_dev);
615 for (cpu = 0; cpu < ncpus; ++cpu) {
616 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu);
619 * Per-cpu messages and event flags.
621 psc->message = hyperv_dmamem_alloc(parent_dtag,
622 PAGE_SIZE, 0, PAGE_SIZE, &psc->message_dma,
623 BUS_DMA_WAITOK | BUS_DMA_ZERO);
624 if (psc->message == NULL)
625 return ENOMEM;
627 psc->event_flags = hyperv_dmamem_alloc(parent_dtag,
628 PAGE_SIZE, 0, PAGE_SIZE, &psc->event_flags_dma,
629 BUS_DMA_WAITOK | BUS_DMA_ZERO);
630 if (psc->event_flags == NULL)
631 return ENOMEM;
634 evtflags = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
635 PAGE_SIZE, &sc->vmbus_evtflags_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
636 if (evtflags == NULL)
637 return ENOMEM;
638 sc->vmbus_rx_evtflags = (u_long *)evtflags;
639 sc->vmbus_tx_evtflags = (u_long *)(evtflags + (PAGE_SIZE / 2));
640 sc->vmbus_evtflags = evtflags;
642 sc->vmbus_mnf1 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
643 PAGE_SIZE, &sc->vmbus_mnf1_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
644 if (sc->vmbus_mnf1 == NULL)
645 return ENOMEM;
647 sc->vmbus_mnf2 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0,
648 PAGE_SIZE, &sc->vmbus_mnf2_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO);
649 if (sc->vmbus_mnf2 == NULL)
650 return ENOMEM;
652 return 0;
655 static void
656 vmbus_dma_free(struct vmbus_softc *sc)
658 int cpu;
660 if (sc->vmbus_evtflags != NULL) {
661 hyperv_dmamem_free(&sc->vmbus_evtflags_dma, sc->vmbus_evtflags);
662 sc->vmbus_evtflags = NULL;
663 sc->vmbus_rx_evtflags = NULL;
664 sc->vmbus_tx_evtflags = NULL;
666 if (sc->vmbus_mnf1 != NULL) {
667 hyperv_dmamem_free(&sc->vmbus_mnf1_dma, sc->vmbus_mnf1);
668 sc->vmbus_mnf1 = NULL;
670 if (sc->vmbus_mnf2 != NULL) {
671 hyperv_dmamem_free(&sc->vmbus_mnf2_dma, sc->vmbus_mnf2);
672 sc->vmbus_mnf2 = NULL;
675 for (cpu = 0; cpu < ncpus; ++cpu) {
676 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu);
678 if (psc->message != NULL) {
679 hyperv_dmamem_free(&psc->message_dma, psc->message);
680 psc->message = NULL;
682 if (psc->event_flags != NULL) {
683 hyperv_dmamem_free(&psc->event_flags_dma,
684 psc->event_flags);
685 psc->event_flags = NULL;
690 static int
691 vmbus_intr_rid(struct resource_list *rl, int rid)
693 do {
694 ++rid;
695 if (resource_list_find(rl, SYS_RES_IRQ, rid) == NULL)
696 break;
697 } while (1);
698 return rid;
701 static int
702 vmbus_intr_setup(struct vmbus_softc *sc)
704 device_t dev = sc->vmbus_dev;
705 device_t parent = device_get_parent(dev);
706 device_t bus = device_get_parent(parent);
707 struct resource_list *rl;
708 int rid, cpu;
710 rl = BUS_GET_RESOURCE_LIST(parent, dev);
711 if (rl == NULL)
712 return ENXIO;
714 rid = 0;
715 for (cpu = 0; cpu < ncpus; ++cpu) {
716 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu);
717 struct resource *res;
718 uint64_t msi_addr;
719 uint32_t msi_data;
720 int irq, error;
722 error = PCIB_ALLOC_MSIX(bus, dev, &irq, cpu);
723 if (error) {
724 device_printf(dev, "alloc vector on cpu%d failed: %d\n",
725 cpu, error);
726 return ENXIO;
728 rid = vmbus_intr_rid(rl, rid);
729 resource_list_add(rl, SYS_RES_IRQ, rid, irq, irq, 1, cpu);
730 psc->intr_rid = rid;
732 res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
733 if (res == NULL) {
734 device_printf(dev, "alloc irq on cpu%d failed: %d\n",
735 cpu, error);
736 return ENXIO;
738 psc->intr_res = res;
740 error = PCIB_MAP_MSI(bus, dev, rman_get_start(res),
741 &msi_addr, &msi_data, cpu);
742 if (error) {
743 device_printf(dev, "map irq on cpu%d failed: %d\n",
744 cpu, error);
745 return ENXIO;
747 psc->intr_vec = hyperv_msi2vector(msi_addr, msi_data);
749 if (bootverbose) {
750 device_printf(dev, "vector %d irq %d on cpu%d\n",
751 psc->intr_vec, irq, cpu);
754 ksnprintf(psc->intr_desc, sizeof(psc->intr_desc), "%s cpu%d",
755 device_get_nameunit(dev), cpu);
756 error = bus_setup_intr_descr(dev, res, INTR_MPSAFE, vmbus_intr,
757 psc, &psc->intr_hand, NULL, psc->intr_desc);
758 if (error) {
759 device_printf(dev, "setup intr on cpu%d failed: %d\n",
760 cpu, error);
761 return ENXIO;
764 return 0;
767 static void
768 vmbus_intr_teardown(struct vmbus_softc *sc)
770 device_t dev = sc->vmbus_dev;
771 device_t parent = device_get_parent(dev);
772 struct resource_list *rl;
773 int cpu;
775 rl = BUS_GET_RESOURCE_LIST(parent, dev);
776 if (rl == NULL)
777 return;
779 for (cpu = 0; cpu < ncpus; ++cpu) {
780 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, cpu);
782 if (psc->intr_hand != NULL) {
783 bus_teardown_intr(dev, psc->intr_res, psc->intr_hand);
784 psc->intr_hand = NULL;
787 if (psc->intr_res != NULL) {
788 bus_release_resource(dev, SYS_RES_IRQ, psc->intr_rid,
789 psc->intr_res);
790 psc->intr_res = NULL;
793 if (psc->intr_rid != 0) {
794 struct resource_list_entry *rle;
795 int irq;
797 rle = resource_list_find(rl, SYS_RES_IRQ,
798 psc->intr_rid);
799 irq = rle->start;
800 resource_list_delete(rl, SYS_RES_IRQ, psc->intr_rid);
802 PCIB_RELEASE_MSIX(device_get_parent(parent), dev, irq,
803 psc->cpuid);
804 psc->intr_rid = 0;
809 static void
810 vmbus_synic_setup(void *xsc)
812 struct vmbus_softc *sc = xsc;
813 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, mycpuid);
814 uint64_t val, orig;
815 uint32_t sint;
817 if (hyperv_features & CPUID_HV_MSR_VP_INDEX) {
819 * Save virtual processor id.
821 psc->vcpuid = rdmsr(MSR_HV_VP_INDEX);
822 } else {
824 * XXX
825 * Virtual processoor id is only used by a pretty broken
826 * channel selection code from storvsc. It's nothing
827 * critical even if CPUID_HV_MSR_VP_INDEX is not set; keep
828 * moving on.
830 psc->vcpuid = mycpuid;
834 * Setup the SynIC message.
836 orig = rdmsr(MSR_HV_SIMP);
837 val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) |
838 ((psc->message_dma.hv_paddr >> PAGE_SHIFT) << MSR_HV_SIMP_PGSHIFT);
839 wrmsr(MSR_HV_SIMP, val);
842 * Setup the SynIC event flags.
844 orig = rdmsr(MSR_HV_SIEFP);
845 val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) |
846 ((psc->event_flags_dma.hv_paddr >> PAGE_SHIFT) <<
847 MSR_HV_SIEFP_PGSHIFT);
848 wrmsr(MSR_HV_SIEFP, val);
852 * Configure and unmask SINT for message and event flags.
854 sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
855 orig = rdmsr(sint);
856 val = psc->intr_vec | /* MSR_HV_SINT_AUTOEOI | notyet */
857 (orig & MSR_HV_SINT_RSVD_MASK);
858 wrmsr(sint, val);
861 * Configure and unmask SINT for timer.
863 sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
864 orig = rdmsr(sint);
865 val = XTIMER_OFFSET | /* MSR_HV_SINT_AUTOEOI | notyet */
866 (orig & MSR_HV_SINT_RSVD_MASK);
867 wrmsr(sint, val);
870 * All done; enable SynIC.
872 orig = rdmsr(MSR_HV_SCONTROL);
873 val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK);
874 wrmsr(MSR_HV_SCONTROL, val);
877 static void
878 vmbus_timer_stop(void *arg __unused)
880 for (;;) {
881 uint64_t val;
883 /* Stop counting, and this also implies disabling STIMER0 */
884 wrmsr(MSR_HV_STIMER0_COUNT, 0);
886 val = rdmsr(MSR_HV_STIMER0_CONFIG);
887 if ((val & MSR_HV_STIMER_CFG_ENABLE) == 0)
888 break;
889 cpu_pause();
893 static void
894 vmbus_timer_config(void *arg __unused)
897 * Make sure that STIMER0 is really disabled before writing
898 * to STIMER0_CONFIG.
900 * "Writing to the configuration register of a timer that
901 * is already enabled may result in undefined behaviour."
903 vmbus_timer_stop(arg);
904 wrmsr(MSR_HV_STIMER0_CONFIG,
905 MSR_HV_STIMER_CFG_AUTOEN | MSR_HV_STIMER0_CFG_SINT);
908 static void
909 vmbus_timer_msgintr(struct vmbus_pcpu_data *psc)
911 volatile struct vmbus_message *msg;
913 msg = psc->message + VMBUS_SINT_TIMER;
914 if (msg->msg_type == HYPERV_MSGTYPE_TIMER_EXPIRED) {
915 msg->msg_type = HYPERV_MSGTYPE_NONE;
918 * Make sure the write to msg_type (i.e. set to
919 * HYPERV_MSGTYPE_NONE) happens before we read the
920 * msg_flags and EOMing. Otherwise, the EOMing will
921 * not deliver any more messages since there is no
922 * empty slot.
924 cpu_mfence();
925 if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) {
927 * This will cause message queue rescan to possibly
928 * deliver another msg from the hypervisor
930 wrmsr(MSR_HV_EOM, 0);
935 static void
936 vmbus_timer_restart(void *xsc)
938 struct vmbus_softc *sc = xsc;
939 struct vmbus_pcpu_data *psc = VMBUS_PCPU(sc, mycpuid);
941 crit_enter();
942 vmbus_timer_msgintr(psc);
943 vmbus_timer_oneshot(psc, rdmsr(MSR_HV_TIME_REF_COUNT) + 1);
944 crit_exit();
947 static void
948 vmbus_synic_teardown(void *arg __unused)
950 uint64_t orig;
951 uint32_t sint;
954 * Disable SynIC.
956 orig = rdmsr(MSR_HV_SCONTROL);
957 wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK));
960 * Mask message and event flags SINT.
962 sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE;
963 orig = rdmsr(sint);
964 wrmsr(sint, orig | MSR_HV_SINT_MASKED);
967 * Mask timer SINT.
969 sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER;
970 orig = rdmsr(sint);
971 wrmsr(sint, orig | MSR_HV_SINT_MASKED);
974 * Teardown SynIC message.
976 orig = rdmsr(MSR_HV_SIMP);
977 wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK));
980 * Teardown SynIC event flags.
982 orig = rdmsr(MSR_HV_SIEFP);
983 wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK));
986 static int
987 vmbus_init_contact(struct vmbus_softc *sc, uint32_t version)
989 struct vmbus_chanmsg_init_contact *req;
990 const struct vmbus_chanmsg_version_resp *resp;
991 const struct vmbus_message *msg;
992 struct vmbus_msghc *mh;
993 int error, supp = 0;
995 mh = vmbus_msghc_get(sc, sizeof(*req));
996 if (mh == NULL)
997 return ENXIO;
999 req = vmbus_msghc_dataptr(mh);
1000 req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_INIT_CONTACT;
1001 req->chm_ver = version;
1002 req->chm_evtflags = sc->vmbus_evtflags_dma.hv_paddr;
1003 req->chm_mnf1 = sc->vmbus_mnf1_dma.hv_paddr;
1004 req->chm_mnf2 = sc->vmbus_mnf2_dma.hv_paddr;
1006 error = vmbus_msghc_exec(sc, mh);
1007 if (error) {
1008 vmbus_msghc_put(sc, mh);
1009 return error;
1012 msg = vmbus_msghc_wait_result(sc, mh);
1013 resp = (const struct vmbus_chanmsg_version_resp *)msg->msg_data;
1014 supp = resp->chm_supp;
1016 vmbus_msghc_put(sc, mh);
1018 return (supp ? 0 : EOPNOTSUPP);
1021 static int
1022 vmbus_init(struct vmbus_softc *sc)
1024 int i;
1026 for (i = 0; i < nitems(vmbus_version); ++i) {
1027 int error;
1029 error = vmbus_init_contact(sc, vmbus_version[i]);
1030 if (!error) {
1031 sc->vmbus_version = vmbus_version[i];
1032 device_printf(sc->vmbus_dev, "version %u.%u\n",
1033 (sc->vmbus_version >> 16),
1034 (sc->vmbus_version & 0xffff));
1035 return 0;
1038 return ENXIO;
1041 static void
1042 vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
1044 const struct vmbus_chanmsg_hdr *hdr;
1046 hdr = (const struct vmbus_chanmsg_hdr *)msg->msg_data;
1048 /* TODO */
1049 if (hdr->chm_type == VMBUS_CHANMSG_TYPE_VERSION_RESP)
1050 vmbus_msghc_wakeup(sc, msg);