1250199Sgrehan/*- 2322612Ssephe * Copyright (c) 2009-2012,2016-2017 Microsoft Corp. 3250199Sgrehan * Copyright (c) 2012 NetApp Inc. 4250199Sgrehan * Copyright (c) 2012 Citrix Inc. 5250199Sgrehan * All rights reserved. 6250199Sgrehan * 7250199Sgrehan * Redistribution and use in source and binary forms, with or without 8250199Sgrehan * modification, are permitted provided that the following conditions 9250199Sgrehan * are met: 10250199Sgrehan * 1. Redistributions of source code must retain the above copyright 11250199Sgrehan * notice unmodified, this list of conditions, and the following 12250199Sgrehan * disclaimer. 13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 14250199Sgrehan * notice, this list of conditions and the following disclaimer in the 15250199Sgrehan * documentation and/or other materials provided with the distribution. 16250199Sgrehan * 17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27250199Sgrehan */ 28250199Sgrehan 29250199Sgrehan/* 30250199Sgrehan * VM Bus Driver Implementation 31250199Sgrehan */ 32256276Sdim#include <sys/cdefs.h> 33256276Sdim__FBSDID("$FreeBSD: stable/11/sys/dev/hyperv/vmbus/vmbus.c 329462 2018-02-17 18:00:01Z kib $"); 34250199Sgrehan 35250199Sgrehan#include <sys/param.h> 36250199Sgrehan#include <sys/bus.h> 37250199Sgrehan#include <sys/kernel.h> 38250199Sgrehan#include <sys/lock.h> 39250199Sgrehan#include <sys/malloc.h> 40250199Sgrehan#include <sys/module.h> 41307466Ssephe#include <sys/mutex.h> 42307466Ssephe#include <sys/smp.h> 43250199Sgrehan#include <sys/sysctl.h> 44250199Sgrehan#include <sys/systm.h> 45250199Sgrehan#include <sys/taskqueue.h> 46250199Sgrehan 47309312Sdexuan#include <machine/bus.h> 48250199Sgrehan#include <machine/intr_machdep.h> 49329462Skib#include <machine/md_var.h> 50309312Sdexuan#include <machine/resource.h> 51307466Ssephe#include <x86/include/apicvar.h> 52250199Sgrehan 53307466Ssephe#include <contrib/dev/acpica/include/acpi.h> 54309312Sdexuan#include <dev/acpica/acpivar.h> 55307466Ssephe 56297142Ssephe#include <dev/hyperv/include/hyperv.h> 57307475Ssephe#include <dev/hyperv/include/vmbus_xact.h> 58300654Ssephe#include <dev/hyperv/vmbus/hyperv_reg.h> 59300834Ssephe#include <dev/hyperv/vmbus/hyperv_var.h> 60301019Ssephe#include <dev/hyperv/vmbus/vmbus_reg.h> 61300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h> 62307463Ssephe#include <dev/hyperv/vmbus/vmbus_chanvar.h> 63250199Sgrehan 64293870Ssephe#include "acpi_if.h" 65309312Sdexuan#include "pcib_if.h" 66307302Ssephe#include "vmbus_if.h" 67250199Sgrehan 68307302Ssephe#define VMBUS_GPADL_START 0xe1e10 69307278Ssephe 70307278Ssephestruct vmbus_msghc { 71307475Ssephe struct vmbus_xact *mh_xact; 72307278Ssephe struct hypercall_postmsg_in mh_inprm_save; 73307278Ssephe}; 74307278Ssephe 75318392Ssephestatic void vmbus_identify(driver_t *, device_t); 76307466Ssephestatic int vmbus_probe(device_t); 77307466Ssephestatic int vmbus_attach(device_t); 78307466Ssephestatic int vmbus_detach(device_t); 79307466Ssephestatic int vmbus_read_ivar(device_t, device_t, int, 80307466Ssephe uintptr_t *); 81307466Ssephestatic int vmbus_child_pnpinfo_str(device_t, device_t, 82307466Ssephe char *, size_t); 83309312Sdexuanstatic struct resource *vmbus_alloc_resource(device_t dev, 84309312Sdexuan device_t child, int type, int *rid, 85309312Sdexuan rman_res_t start, rman_res_t end, 86309312Sdexuan rman_res_t count, u_int flags); 87309312Sdexuanstatic int vmbus_alloc_msi(device_t bus, device_t dev, 88309312Sdexuan int count, int maxcount, int *irqs); 89309312Sdexuanstatic int vmbus_release_msi(device_t bus, device_t dev, 90309312Sdexuan int count, int *irqs); 91309312Sdexuanstatic int vmbus_alloc_msix(device_t bus, device_t dev, 92309312Sdexuan int *irq); 93309312Sdexuanstatic int vmbus_release_msix(device_t bus, device_t dev, 94309312Sdexuan int irq); 95309312Sdexuanstatic int vmbus_map_msi(device_t bus, device_t dev, 96309312Sdexuan int irq, uint64_t *addr, uint32_t *data); 97307466Ssephestatic uint32_t vmbus_get_version_method(device_t, device_t); 98307466Ssephestatic int vmbus_probe_guid_method(device_t, device_t, 99307466Ssephe const struct hyperv_guid *); 100309312Sdexuanstatic uint32_t vmbus_get_vcpu_id_method(device_t bus, 101309312Sdexuan device_t dev, int cpu); 102311368Ssephestatic struct taskqueue *vmbus_get_eventtq_method(device_t, device_t, 103311368Ssephe int); 104310573Ssephe#ifdef EARLY_AP_STARTUP 105310573Ssephestatic void vmbus_intrhook(void *); 106310573Ssephe#endif 107307466Ssephe 108307278Ssephestatic int vmbus_init(struct vmbus_softc *); 109307302Ssephestatic int vmbus_connect(struct vmbus_softc *, uint32_t); 110307291Ssephestatic int vmbus_req_channels(struct vmbus_softc *sc); 111307302Ssephestatic void vmbus_disconnect(struct vmbus_softc *); 112307291Ssephestatic int vmbus_scan(struct vmbus_softc *); 113307599Ssephestatic void vmbus_scan_teardown(struct vmbus_softc *); 114307451Ssephestatic void vmbus_scan_done(struct vmbus_softc *, 115307451Ssephe const struct vmbus_message *); 116307451Ssephestatic void vmbus_chanmsg_handle(struct vmbus_softc *, 117307451Ssephe const struct vmbus_message *); 118307466Ssephestatic void vmbus_msg_task(void *, int); 119307466Ssephestatic void vmbus_synic_setup(void *); 120307466Ssephestatic void vmbus_synic_teardown(void *); 121307291Ssephestatic int vmbus_sysctl_version(SYSCTL_HANDLER_ARGS); 122307466Ssephestatic int vmbus_dma_alloc(struct vmbus_softc *); 123307466Ssephestatic void vmbus_dma_free(struct vmbus_softc *); 124307466Ssephestatic int vmbus_intr_setup(struct vmbus_softc *); 125307466Ssephestatic void vmbus_intr_teardown(struct vmbus_softc *); 126307466Ssephestatic int vmbus_doattach(struct vmbus_softc *); 127307466Ssephestatic void vmbus_event_proc_dummy(struct vmbus_softc *, 128307466Ssephe int); 129307291Ssephe 130307466Ssephestatic struct vmbus_softc *vmbus_sc; 131300102Ssephe 132324576SsepheSYSCTL_NODE(_hw, OID_AUTO, vmbus, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 133324576Ssephe "Hyper-V vmbus"); 134324576Ssephe 135324576Ssephestatic int vmbus_pin_evttask = 1; 136324576SsepheSYSCTL_INT(_hw_vmbus, OID_AUTO, pin_evttask, CTLFLAG_RDTUN, 137324576Ssephe &vmbus_pin_evttask, 0, "Pin event tasks to their respective CPU"); 138324576Ssephe 139329462Skibextern inthand_t IDTVEC(vmbus_isr), IDTVEC(vmbus_isr_pti); 140300574Ssephe 141307278Ssephestatic const uint32_t vmbus_version[] = { 142307302Ssephe VMBUS_VERSION_WIN8_1, 143307302Ssephe VMBUS_VERSION_WIN8, 144307302Ssephe VMBUS_VERSION_WIN7, 145307302Ssephe VMBUS_VERSION_WS2008 146307278Ssephe}; 147307278Ssephe 148307451Ssephestatic const vmbus_chanmsg_proc_t 149307451Ssephevmbus_chanmsg_handlers[VMBUS_CHANMSG_TYPE_MAX] = { 150307451Ssephe VMBUS_CHANMSG_PROC(CHOFFER_DONE, vmbus_scan_done), 151307451Ssephe VMBUS_CHANMSG_PROC_WAKEUP(CONNECT_RESP) 152307451Ssephe}; 153307451Ssephe 154307466Ssephestatic device_method_t vmbus_methods[] = { 155307466Ssephe /* Device interface */ 156318392Ssephe DEVMETHOD(device_identify, vmbus_identify), 157307466Ssephe DEVMETHOD(device_probe, vmbus_probe), 158307466Ssephe DEVMETHOD(device_attach, vmbus_attach), 159307466Ssephe DEVMETHOD(device_detach, vmbus_detach), 160307466Ssephe DEVMETHOD(device_shutdown, bus_generic_shutdown), 161307466Ssephe DEVMETHOD(device_suspend, bus_generic_suspend), 162307466Ssephe DEVMETHOD(device_resume, bus_generic_resume), 163307466Ssephe 164307466Ssephe /* Bus interface */ 165307466Ssephe DEVMETHOD(bus_add_child, bus_generic_add_child), 166307466Ssephe DEVMETHOD(bus_print_child, bus_generic_print_child), 167307466Ssephe DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 168307466Ssephe DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str), 169309312Sdexuan DEVMETHOD(bus_alloc_resource, vmbus_alloc_resource), 170309312Sdexuan DEVMETHOD(bus_release_resource, bus_generic_release_resource), 171309312Sdexuan DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 172309312Sdexuan DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 173309312Sdexuan DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 174309312Sdexuan DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 175309312Sdexuan#if __FreeBSD_version >= 1100000 176309312Sdexuan DEVMETHOD(bus_get_cpus, bus_generic_get_cpus), 177309312Sdexuan#endif 178307466Ssephe 179309312Sdexuan /* pcib interface */ 180309312Sdexuan DEVMETHOD(pcib_alloc_msi, vmbus_alloc_msi), 181309312Sdexuan DEVMETHOD(pcib_release_msi, vmbus_release_msi), 182309312Sdexuan DEVMETHOD(pcib_alloc_msix, vmbus_alloc_msix), 183309312Sdexuan DEVMETHOD(pcib_release_msix, vmbus_release_msix), 184309312Sdexuan DEVMETHOD(pcib_map_msi, vmbus_map_msi), 185309312Sdexuan 186307466Ssephe /* Vmbus interface */ 187307466Ssephe DEVMETHOD(vmbus_get_version, vmbus_get_version_method), 188307466Ssephe DEVMETHOD(vmbus_probe_guid, vmbus_probe_guid_method), 189309312Sdexuan DEVMETHOD(vmbus_get_vcpu_id, vmbus_get_vcpu_id_method), 190311368Ssephe DEVMETHOD(vmbus_get_event_taskq, vmbus_get_eventtq_method), 191307466Ssephe 192307466Ssephe DEVMETHOD_END 193307466Ssephe}; 194307466Ssephe 195307466Ssephestatic driver_t vmbus_driver = { 196307466Ssephe "vmbus", 197307466Ssephe vmbus_methods, 198307466Ssephe sizeof(struct vmbus_softc) 199307466Ssephe}; 200307466Ssephe 201307466Ssephestatic devclass_t vmbus_devclass; 202307466Ssephe 203318392SsepheDRIVER_MODULE(vmbus, pcib, vmbus_driver, vmbus_devclass, NULL, NULL); 204318392SsepheDRIVER_MODULE(vmbus, acpi_syscontainer, vmbus_driver, vmbus_devclass, 205318392Ssephe NULL, NULL); 206318392Ssephe 207307466SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1); 208309312SdexuanMODULE_DEPEND(vmbus, pci, 1, 1, 1); 209307466SsepheMODULE_VERSION(vmbus, 1); 210307466Ssephe 211307466Ssephestatic __inline struct vmbus_softc * 212307466Ssephevmbus_get_softc(void) 213307466Ssephe{ 214307466Ssephe return vmbus_sc; 215307466Ssephe} 216307466Ssephe 217307301Ssephevoid 218307301Ssephevmbus_msghc_reset(struct vmbus_msghc *mh, size_t dsize) 219307301Ssephe{ 220307301Ssephe struct hypercall_postmsg_in *inprm; 221307301Ssephe 222307301Ssephe if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX) 223307301Ssephe panic("invalid data size %zu", dsize); 224307301Ssephe 225307475Ssephe inprm = vmbus_xact_req_data(mh->mh_xact); 226307301Ssephe memset(inprm, 0, HYPERCALL_POSTMSGIN_SIZE); 227307301Ssephe inprm->hc_connid = VMBUS_CONNID_MESSAGE; 228307301Ssephe inprm->hc_msgtype = HYPERV_MSGTYPE_CHANNEL; 229307301Ssephe inprm->hc_dsize = dsize; 230307301Ssephe} 231307301Ssephe 232307278Ssephestruct vmbus_msghc * 233307278Ssephevmbus_msghc_get(struct vmbus_softc *sc, size_t dsize) 234307278Ssephe{ 235307278Ssephe struct vmbus_msghc *mh; 236307475Ssephe struct vmbus_xact *xact; 237307278Ssephe 238307278Ssephe if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX) 239307301Ssephe panic("invalid data size %zu", dsize); 240307278Ssephe 241307475Ssephe xact = vmbus_xact_get(sc->vmbus_xc, 242307475Ssephe dsize + __offsetof(struct hypercall_postmsg_in, hc_data[0])); 243307475Ssephe if (xact == NULL) 244307475Ssephe return (NULL); 245307278Ssephe 246307475Ssephe mh = vmbus_xact_priv(xact, sizeof(*mh)); 247307475Ssephe mh->mh_xact = xact; 248307475Ssephe 249307301Ssephe vmbus_msghc_reset(mh, dsize); 250307475Ssephe return (mh); 251307278Ssephe} 252307278Ssephe 253307278Ssephevoid 254307475Ssephevmbus_msghc_put(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh) 255307278Ssephe{ 256307278Ssephe 257307475Ssephe vmbus_xact_put(mh->mh_xact); 258307278Ssephe} 259307278Ssephe 260307278Ssephevoid * 261307278Ssephevmbus_msghc_dataptr(struct vmbus_msghc *mh) 262307278Ssephe{ 263307475Ssephe struct hypercall_postmsg_in *inprm; 264307278Ssephe 265307475Ssephe inprm = vmbus_xact_req_data(mh->mh_xact); 266307475Ssephe return (inprm->hc_data); 267307278Ssephe} 268307278Ssephe 269307278Ssepheint 270307278Ssephevmbus_msghc_exec_noresult(struct vmbus_msghc *mh) 271307278Ssephe{ 272307278Ssephe sbintime_t time = SBT_1MS; 273307475Ssephe struct hypercall_postmsg_in *inprm; 274307475Ssephe bus_addr_t inprm_paddr; 275307278Ssephe int i; 276307278Ssephe 277307475Ssephe inprm = vmbus_xact_req_data(mh->mh_xact); 278307475Ssephe inprm_paddr = vmbus_xact_req_paddr(mh->mh_xact); 279307475Ssephe 280307278Ssephe /* 281307278Ssephe * Save the input parameter so that we could restore the input 282307278Ssephe * parameter if the Hypercall failed. 283307278Ssephe * 284307278Ssephe * XXX 285307278Ssephe * Is this really necessary?! i.e. Will the Hypercall ever 286307278Ssephe * overwrite the input parameter? 287307278Ssephe */ 288307475Ssephe memcpy(&mh->mh_inprm_save, inprm, HYPERCALL_POSTMSGIN_SIZE); 289307278Ssephe 290307278Ssephe /* 291307278Ssephe * In order to cope with transient failures, e.g. insufficient 292307278Ssephe * resources on host side, we retry the post message Hypercall 293307278Ssephe * several times. 20 retries seem sufficient. 294307278Ssephe */ 295307278Ssephe#define HC_RETRY_MAX 20 296307278Ssephe 297307278Ssephe for (i = 0; i < HC_RETRY_MAX; ++i) { 298307278Ssephe uint64_t status; 299307278Ssephe 300307475Ssephe status = hypercall_post_message(inprm_paddr); 301307278Ssephe if (status == HYPERCALL_STATUS_SUCCESS) 302307278Ssephe return 0; 303307278Ssephe 304307278Ssephe pause_sbt("hcpmsg", time, 0, C_HARDCLOCK); 305307278Ssephe if (time < SBT_1S * 2) 306307278Ssephe time *= 2; 307307278Ssephe 308307278Ssephe /* Restore input parameter and try again */ 309307475Ssephe memcpy(inprm, &mh->mh_inprm_save, HYPERCALL_POSTMSGIN_SIZE); 310307278Ssephe } 311307278Ssephe 312307278Ssephe#undef HC_RETRY_MAX 313307278Ssephe 314307278Ssephe return EIO; 315307278Ssephe} 316307278Ssephe 317307278Ssepheint 318307475Ssephevmbus_msghc_exec(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh) 319307278Ssephe{ 320307278Ssephe int error; 321307278Ssephe 322307475Ssephe vmbus_xact_activate(mh->mh_xact); 323307278Ssephe error = vmbus_msghc_exec_noresult(mh); 324307475Ssephe if (error) 325307475Ssephe vmbus_xact_deactivate(mh->mh_xact); 326307278Ssephe return error; 327307278Ssephe} 328307278Ssephe 329311367Ssephevoid 330311367Ssephevmbus_msghc_exec_cancel(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh) 331311367Ssephe{ 332311367Ssephe 333311367Ssephe vmbus_xact_deactivate(mh->mh_xact); 334311367Ssephe} 335311367Ssephe 336307278Ssepheconst struct vmbus_message * 337307475Ssephevmbus_msghc_wait_result(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh) 338307278Ssephe{ 339307475Ssephe size_t resp_len; 340307278Ssephe 341307475Ssephe return (vmbus_xact_wait(mh->mh_xact, &resp_len)); 342307278Ssephe} 343307278Ssephe 344311367Ssepheconst struct vmbus_message * 345311367Ssephevmbus_msghc_poll_result(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh) 346311367Ssephe{ 347311367Ssephe size_t resp_len; 348311367Ssephe 349311367Ssephe return (vmbus_xact_poll(mh->mh_xact, &resp_len)); 350311367Ssephe} 351311367Ssephe 352307278Ssephevoid 353307278Ssephevmbus_msghc_wakeup(struct vmbus_softc *sc, const struct vmbus_message *msg) 354307278Ssephe{ 355307278Ssephe 356307475Ssephe vmbus_xact_ctx_wakeup(sc->vmbus_xc, msg, sizeof(*msg)); 357307278Ssephe} 358307278Ssephe 359307302Ssepheuint32_t 360307302Ssephevmbus_gpadl_alloc(struct vmbus_softc *sc) 361307302Ssephe{ 362311366Ssephe uint32_t gpadl; 363311366Ssephe 364311366Ssepheagain: 365311366Ssephe gpadl = atomic_fetchadd_int(&sc->vmbus_gpadl, 1); 366311366Ssephe if (gpadl == 0) 367311366Ssephe goto again; 368311366Ssephe return (gpadl); 369307302Ssephe} 370307302Ssephe 371307278Ssephestatic int 372307302Ssephevmbus_connect(struct vmbus_softc *sc, uint32_t version) 373307278Ssephe{ 374307302Ssephe struct vmbus_chanmsg_connect *req; 375307278Ssephe const struct vmbus_message *msg; 376307278Ssephe struct vmbus_msghc *mh; 377307302Ssephe int error, done = 0; 378307278Ssephe 379307278Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 380307278Ssephe if (mh == NULL) 381307278Ssephe return ENXIO; 382307278Ssephe 383307278Ssephe req = vmbus_msghc_dataptr(mh); 384307302Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CONNECT; 385307278Ssephe req->chm_ver = version; 386307278Ssephe req->chm_evtflags = sc->vmbus_evtflags_dma.hv_paddr; 387307278Ssephe req->chm_mnf1 = sc->vmbus_mnf1_dma.hv_paddr; 388307278Ssephe req->chm_mnf2 = sc->vmbus_mnf2_dma.hv_paddr; 389307278Ssephe 390307278Ssephe error = vmbus_msghc_exec(sc, mh); 391307278Ssephe if (error) { 392307278Ssephe vmbus_msghc_put(sc, mh); 393307278Ssephe return error; 394307278Ssephe } 395307278Ssephe 396307278Ssephe msg = vmbus_msghc_wait_result(sc, mh); 397307302Ssephe done = ((const struct vmbus_chanmsg_connect_resp *) 398307302Ssephe msg->msg_data)->chm_done; 399307278Ssephe 400307278Ssephe vmbus_msghc_put(sc, mh); 401307278Ssephe 402307302Ssephe return (done ? 0 : EOPNOTSUPP); 403307278Ssephe} 404307278Ssephe 405307278Ssephestatic int 406307278Ssephevmbus_init(struct vmbus_softc *sc) 407307278Ssephe{ 408307278Ssephe int i; 409307278Ssephe 410307278Ssephe for (i = 0; i < nitems(vmbus_version); ++i) { 411307278Ssephe int error; 412307278Ssephe 413307302Ssephe error = vmbus_connect(sc, vmbus_version[i]); 414307278Ssephe if (!error) { 415307302Ssephe sc->vmbus_version = vmbus_version[i]; 416307278Ssephe device_printf(sc->vmbus_dev, "version %u.%u\n", 417307302Ssephe VMBUS_VERSION_MAJOR(sc->vmbus_version), 418307302Ssephe VMBUS_VERSION_MINOR(sc->vmbus_version)); 419307278Ssephe return 0; 420307278Ssephe } 421307278Ssephe } 422307278Ssephe return ENXIO; 423307278Ssephe} 424307278Ssephe 425307278Ssephestatic void 426307302Ssephevmbus_disconnect(struct vmbus_softc *sc) 427307291Ssephe{ 428307302Ssephe struct vmbus_chanmsg_disconnect *req; 429307291Ssephe struct vmbus_msghc *mh; 430307291Ssephe int error; 431307291Ssephe 432307291Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 433307291Ssephe if (mh == NULL) { 434307291Ssephe device_printf(sc->vmbus_dev, 435307302Ssephe "can not get msg hypercall for disconnect\n"); 436307291Ssephe return; 437307291Ssephe } 438307291Ssephe 439307291Ssephe req = vmbus_msghc_dataptr(mh); 440307302Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_DISCONNECT; 441307291Ssephe 442307291Ssephe error = vmbus_msghc_exec_noresult(mh); 443307291Ssephe vmbus_msghc_put(sc, mh); 444307291Ssephe 445307291Ssephe if (error) { 446307291Ssephe device_printf(sc->vmbus_dev, 447307302Ssephe "disconnect msg hypercall failed\n"); 448307291Ssephe } 449307291Ssephe} 450307291Ssephe 451307291Ssephestatic int 452307291Ssephevmbus_req_channels(struct vmbus_softc *sc) 453307291Ssephe{ 454307302Ssephe struct vmbus_chanmsg_chrequest *req; 455307291Ssephe struct vmbus_msghc *mh; 456307291Ssephe int error; 457307291Ssephe 458307291Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 459307291Ssephe if (mh == NULL) 460307291Ssephe return ENXIO; 461307291Ssephe 462307291Ssephe req = vmbus_msghc_dataptr(mh); 463307302Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHREQUEST; 464307291Ssephe 465307291Ssephe error = vmbus_msghc_exec_noresult(mh); 466307291Ssephe vmbus_msghc_put(sc, mh); 467307291Ssephe 468307291Ssephe return error; 469307291Ssephe} 470307291Ssephe 471307451Ssephestatic void 472307599Ssephevmbus_scan_done_task(void *xsc, int pending __unused) 473307291Ssephe{ 474307599Ssephe struct vmbus_softc *sc = xsc; 475307599Ssephe 476307599Ssephe mtx_lock(&Giant); 477307599Ssephe sc->vmbus_scandone = true; 478307599Ssephe mtx_unlock(&Giant); 479307599Ssephe wakeup(&sc->vmbus_scandone); 480307291Ssephe} 481307291Ssephe 482307451Ssephestatic void 483307451Ssephevmbus_scan_done(struct vmbus_softc *sc, 484307451Ssephe const struct vmbus_message *msg __unused) 485307291Ssephe{ 486307291Ssephe 487307599Ssephe taskqueue_enqueue(sc->vmbus_devtq, &sc->vmbus_scandone_task); 488307291Ssephe} 489307291Ssephe 490307291Ssephestatic int 491307291Ssephevmbus_scan(struct vmbus_softc *sc) 492307291Ssephe{ 493307291Ssephe int error; 494307291Ssephe 495307291Ssephe /* 496307599Ssephe * Identify, probe and attach for non-channel devices. 497307599Ssephe */ 498307599Ssephe bus_generic_probe(sc->vmbus_dev); 499307599Ssephe bus_generic_attach(sc->vmbus_dev); 500307599Ssephe 501307599Ssephe /* 502307599Ssephe * This taskqueue serializes vmbus devices' attach and detach 503307599Ssephe * for channel offer and rescind messages. 504307599Ssephe */ 505307599Ssephe sc->vmbus_devtq = taskqueue_create("vmbus dev", M_WAITOK, 506307599Ssephe taskqueue_thread_enqueue, &sc->vmbus_devtq); 507307599Ssephe taskqueue_start_threads(&sc->vmbus_devtq, 1, PI_NET, "vmbusdev"); 508307599Ssephe TASK_INIT(&sc->vmbus_scandone_task, 0, vmbus_scan_done_task, sc); 509307599Ssephe 510307599Ssephe /* 511307599Ssephe * This taskqueue handles sub-channel detach, so that vmbus 512307599Ssephe * device's detach running in vmbus_devtq can drain its sub- 513307599Ssephe * channels. 514307599Ssephe */ 515307599Ssephe sc->vmbus_subchtq = taskqueue_create("vmbus subch", M_WAITOK, 516307599Ssephe taskqueue_thread_enqueue, &sc->vmbus_subchtq); 517307599Ssephe taskqueue_start_threads(&sc->vmbus_subchtq, 1, PI_NET, "vmbussch"); 518307599Ssephe 519307599Ssephe /* 520307291Ssephe * Start vmbus scanning. 521307291Ssephe */ 522307291Ssephe error = vmbus_req_channels(sc); 523307291Ssephe if (error) { 524307291Ssephe device_printf(sc->vmbus_dev, "channel request failed: %d\n", 525307291Ssephe error); 526307599Ssephe return (error); 527307291Ssephe } 528307291Ssephe 529307291Ssephe /* 530307599Ssephe * Wait for all vmbus devices from the initial channel offers to be 531307599Ssephe * attached. 532307291Ssephe */ 533307599Ssephe GIANT_REQUIRED; 534307599Ssephe while (!sc->vmbus_scandone) 535307599Ssephe mtx_sleep(&sc->vmbus_scandone, &Giant, 0, "vmbusdev", 0); 536307291Ssephe 537307291Ssephe if (bootverbose) { 538307291Ssephe device_printf(sc->vmbus_dev, "device scan, probe and attach " 539307291Ssephe "done\n"); 540307291Ssephe } 541307599Ssephe return (0); 542307291Ssephe} 543307291Ssephe 544307291Ssephestatic void 545307599Ssephevmbus_scan_teardown(struct vmbus_softc *sc) 546307599Ssephe{ 547307599Ssephe 548307599Ssephe GIANT_REQUIRED; 549307599Ssephe if (sc->vmbus_devtq != NULL) { 550307599Ssephe mtx_unlock(&Giant); 551307599Ssephe taskqueue_free(sc->vmbus_devtq); 552307599Ssephe mtx_lock(&Giant); 553307599Ssephe sc->vmbus_devtq = NULL; 554307599Ssephe } 555307599Ssephe if (sc->vmbus_subchtq != NULL) { 556307599Ssephe mtx_unlock(&Giant); 557307599Ssephe taskqueue_free(sc->vmbus_subchtq); 558307599Ssephe mtx_lock(&Giant); 559307599Ssephe sc->vmbus_subchtq = NULL; 560307599Ssephe } 561307599Ssephe} 562307599Ssephe 563307599Ssephestatic void 564307451Ssephevmbus_chanmsg_handle(struct vmbus_softc *sc, const struct vmbus_message *msg) 565307451Ssephe{ 566307451Ssephe vmbus_chanmsg_proc_t msg_proc; 567307451Ssephe uint32_t msg_type; 568307451Ssephe 569307451Ssephe msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type; 570307451Ssephe if (msg_type >= VMBUS_CHANMSG_TYPE_MAX) { 571307451Ssephe device_printf(sc->vmbus_dev, "unknown message type 0x%x\n", 572307451Ssephe msg_type); 573307451Ssephe return; 574307451Ssephe } 575307451Ssephe 576307451Ssephe msg_proc = vmbus_chanmsg_handlers[msg_type]; 577307451Ssephe if (msg_proc != NULL) 578307451Ssephe msg_proc(sc, msg); 579307451Ssephe 580307451Ssephe /* Channel specific processing */ 581307451Ssephe vmbus_chan_msgproc(sc, msg); 582307451Ssephe} 583307451Ssephe 584307451Ssephestatic void 585300572Ssephevmbus_msg_task(void *xsc, int pending __unused) 586250199Sgrehan{ 587300572Ssephe struct vmbus_softc *sc = xsc; 588301019Ssephe volatile struct vmbus_message *msg; 589250199Sgrehan 590300988Ssephe msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE; 591300108Ssephe for (;;) { 592307278Ssephe if (msg->msg_type == HYPERV_MSGTYPE_NONE) { 593301484Ssephe /* No message */ 594301484Ssephe break; 595307278Ssephe } else if (msg->msg_type == HYPERV_MSGTYPE_CHANNEL) { 596301487Ssephe /* Channel message */ 597307451Ssephe vmbus_chanmsg_handle(sc, 598301488Ssephe __DEVOLATILE(const struct vmbus_message *, msg)); 599301484Ssephe } 600292861Sdelphij 601307278Ssephe msg->msg_type = HYPERV_MSGTYPE_NONE; 602300108Ssephe /* 603301019Ssephe * Make sure the write to msg_type (i.e. set to 604307278Ssephe * HYPERV_MSGTYPE_NONE) happens before we read the 605301019Ssephe * msg_flags and EOMing. Otherwise, the EOMing will 606301019Ssephe * not deliver any more messages since there is no 607301019Ssephe * empty slot 608300108Ssephe * 609300108Ssephe * NOTE: 610300108Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 611300108Ssephe * will become compiler fence on UP kernel. 612300108Ssephe */ 613300108Ssephe mb(); 614301019Ssephe if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) { 615250199Sgrehan /* 616250199Sgrehan * This will cause message queue rescan to possibly 617250199Sgrehan * deliver another msg from the hypervisor 618250199Sgrehan */ 619300830Ssephe wrmsr(MSR_HV_EOM, 0); 620300108Ssephe } 621250199Sgrehan } 622250199Sgrehan} 623250199Sgrehan 624301015Ssephestatic __inline int 625301015Ssephevmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu) 626250199Sgrehan{ 627301019Ssephe volatile struct vmbus_message *msg; 628301019Ssephe struct vmbus_message *msg_base; 629250199Sgrehan 630301009Ssephe msg_base = VMBUS_PCPU_GET(sc, message, cpu); 631301009Ssephe 632250199Sgrehan /* 633301009Ssephe * Check event timer. 634301009Ssephe * 635301009Ssephe * TODO: move this to independent IDT vector. 636250199Sgrehan */ 637300988Ssephe msg = msg_base + VMBUS_SINT_TIMER; 638307278Ssephe if (msg->msg_type == HYPERV_MSGTYPE_TIMER_EXPIRED) { 639307278Ssephe msg->msg_type = HYPERV_MSGTYPE_NONE; 640293873Ssephe 641300988Ssephe vmbus_et_intr(frame); 642297176Ssephe 643293873Ssephe /* 644301019Ssephe * Make sure the write to msg_type (i.e. set to 645307278Ssephe * HYPERV_MSGTYPE_NONE) happens before we read the 646301019Ssephe * msg_flags and EOMing. Otherwise, the EOMing will 647301019Ssephe * not deliver any more messages since there is no 648301019Ssephe * empty slot 649297634Ssephe * 650297634Ssephe * NOTE: 651297634Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 652297636Ssephe * will become compiler fence on UP kernel. 653293873Ssephe */ 654297634Ssephe mb(); 655301019Ssephe if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) { 656293873Ssephe /* 657293873Ssephe * This will cause message queue rescan to possibly 658293873Ssephe * deliver another msg from the hypervisor 659293873Ssephe */ 660300830Ssephe wrmsr(MSR_HV_EOM, 0); 661293873Ssephe } 662293873Ssephe } 663293873Ssephe 664301009Ssephe /* 665301009Ssephe * Check events. Hot path for network and storage I/O data; high rate. 666301009Ssephe * 667301009Ssephe * NOTE: 668301009Ssephe * As recommended by the Windows guest fellows, we check events before 669301009Ssephe * checking messages. 670301009Ssephe */ 671301009Ssephe sc->vmbus_event_proc(sc, cpu); 672301009Ssephe 673301009Ssephe /* 674301009Ssephe * Check messages. Mainly management stuffs; ultra low rate. 675301009Ssephe */ 676300988Ssephe msg = msg_base + VMBUS_SINT_MESSAGE; 677307278Ssephe if (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) { 678300646Ssephe taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu), 679300646Ssephe VMBUS_PCPU_PTR(sc, message_task, cpu)); 680250199Sgrehan } 681250199Sgrehan 682293873Ssephe return (FILTER_HANDLED); 683250199Sgrehan} 684250199Sgrehan 685282212Swhuvoid 686301015Ssephevmbus_handle_intr(struct trapframe *trap_frame) 687282212Swhu{ 688300565Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 689300565Ssephe int cpu = curcpu; 690282212Swhu 691282212Swhu /* 692282212Swhu * Disable preemption. 693282212Swhu */ 694282212Swhu critical_enter(); 695282212Swhu 696282212Swhu /* 697282212Swhu * Do a little interrupt counting. 698282212Swhu */ 699300573Ssephe (*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++; 700282212Swhu 701301015Ssephe vmbus_handle_intr1(sc, trap_frame, cpu); 702282212Swhu 703282212Swhu /* 704282212Swhu * Enable preemption. 705282212Swhu */ 706282212Swhu critical_exit(); 707282212Swhu} 708282212Swhu 709300571Ssephestatic void 710300652Ssephevmbus_synic_setup(void *xsc) 711300571Ssephe{ 712300652Ssephe struct vmbus_softc *sc = xsc; 713300654Ssephe int cpu = curcpu; 714300654Ssephe uint64_t val, orig; 715300654Ssephe uint32_t sint; 716300571Ssephe 717300834Ssephe if (hyperv_features & CPUID_HV_MSR_VP_INDEX) { 718307454Ssephe /* Save virtual processor id. */ 719300834Ssephe VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(MSR_HV_VP_INDEX); 720300834Ssephe } else { 721307454Ssephe /* Set virtual processor id to 0 for compatibility. */ 722307454Ssephe VMBUS_PCPU_GET(sc, vcpuid, cpu) = 0; 723300834Ssephe } 724300571Ssephe 725300571Ssephe /* 726300654Ssephe * Setup the SynIC message. 727300571Ssephe */ 728300654Ssephe orig = rdmsr(MSR_HV_SIMP); 729300654Ssephe val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) | 730300654Ssephe ((VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT) << 731300654Ssephe MSR_HV_SIMP_PGSHIFT); 732300654Ssephe wrmsr(MSR_HV_SIMP, val); 733300571Ssephe 734300571Ssephe /* 735300654Ssephe * Setup the SynIC event flags. 736300571Ssephe */ 737300654Ssephe orig = rdmsr(MSR_HV_SIEFP); 738300654Ssephe val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) | 739301106Ssephe ((VMBUS_PCPU_GET(sc, event_flags_dma.hv_paddr, cpu) 740301106Ssephe >> PAGE_SHIFT) << MSR_HV_SIEFP_PGSHIFT); 741300654Ssephe wrmsr(MSR_HV_SIEFP, val); 742300571Ssephe 743300571Ssephe 744300654Ssephe /* 745300654Ssephe * Configure and unmask SINT for message and event flags. 746300654Ssephe */ 747300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE; 748300654Ssephe orig = rdmsr(sint); 749300654Ssephe val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI | 750300654Ssephe (orig & MSR_HV_SINT_RSVD_MASK); 751300654Ssephe wrmsr(sint, val); 752300571Ssephe 753300654Ssephe /* 754300654Ssephe * Configure and unmask SINT for timer. 755300654Ssephe */ 756300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER; 757300654Ssephe orig = rdmsr(sint); 758300654Ssephe val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI | 759300654Ssephe (orig & MSR_HV_SINT_RSVD_MASK); 760300654Ssephe wrmsr(sint, val); 761300571Ssephe 762300571Ssephe /* 763300654Ssephe * All done; enable SynIC. 764300571Ssephe */ 765300654Ssephe orig = rdmsr(MSR_HV_SCONTROL); 766300654Ssephe val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK); 767300654Ssephe wrmsr(MSR_HV_SCONTROL, val); 768300571Ssephe} 769300571Ssephe 770300571Ssephestatic void 771300571Ssephevmbus_synic_teardown(void *arg) 772300571Ssephe{ 773300654Ssephe uint64_t orig; 774300654Ssephe uint32_t sint; 775300571Ssephe 776300654Ssephe /* 777300654Ssephe * Disable SynIC. 778300654Ssephe */ 779300654Ssephe orig = rdmsr(MSR_HV_SCONTROL); 780300654Ssephe wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK)); 781300571Ssephe 782300654Ssephe /* 783300654Ssephe * Mask message and event flags SINT. 784300654Ssephe */ 785300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE; 786300654Ssephe orig = rdmsr(sint); 787300654Ssephe wrmsr(sint, orig | MSR_HV_SINT_MASKED); 788300571Ssephe 789300571Ssephe /* 790300654Ssephe * Mask timer SINT. 791300571Ssephe */ 792300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER; 793300654Ssephe orig = rdmsr(sint); 794300654Ssephe wrmsr(sint, orig | MSR_HV_SINT_MASKED); 795300571Ssephe 796300654Ssephe /* 797300654Ssephe * Teardown SynIC message. 798300654Ssephe */ 799300654Ssephe orig = rdmsr(MSR_HV_SIMP); 800300654Ssephe wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK)); 801300571Ssephe 802300571Ssephe /* 803300654Ssephe * Teardown SynIC event flags. 804300571Ssephe */ 805300654Ssephe orig = rdmsr(MSR_HV_SIEFP); 806300654Ssephe wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK)); 807300571Ssephe} 808300571Ssephe 809300644Ssephestatic int 810300572Ssephevmbus_dma_alloc(struct vmbus_softc *sc) 811300572Ssephe{ 812301583Ssephe bus_dma_tag_t parent_dtag; 813301583Ssephe uint8_t *evtflags; 814300572Ssephe int cpu; 815300572Ssephe 816301583Ssephe parent_dtag = bus_get_dma_tag(sc->vmbus_dev); 817300572Ssephe CPU_FOREACH(cpu) { 818300644Ssephe void *ptr; 819300644Ssephe 820300572Ssephe /* 821300572Ssephe * Per-cpu messages and event flags. 822300572Ssephe */ 823301583Ssephe ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, 824301583Ssephe PAGE_SIZE, VMBUS_PCPU_PTR(sc, message_dma, cpu), 825300572Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 826300644Ssephe if (ptr == NULL) 827300644Ssephe return ENOMEM; 828300644Ssephe VMBUS_PCPU_GET(sc, message, cpu) = ptr; 829300644Ssephe 830301583Ssephe ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, 831301583Ssephe PAGE_SIZE, VMBUS_PCPU_PTR(sc, event_flags_dma, cpu), 832300572Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 833300644Ssephe if (ptr == NULL) 834300644Ssephe return ENOMEM; 835301106Ssephe VMBUS_PCPU_GET(sc, event_flags, cpu) = ptr; 836300572Ssephe } 837301583Ssephe 838301583Ssephe evtflags = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, 839301583Ssephe PAGE_SIZE, &sc->vmbus_evtflags_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); 840301583Ssephe if (evtflags == NULL) 841301583Ssephe return ENOMEM; 842301583Ssephe sc->vmbus_rx_evtflags = (u_long *)evtflags; 843301588Ssephe sc->vmbus_tx_evtflags = (u_long *)(evtflags + (PAGE_SIZE / 2)); 844301583Ssephe sc->vmbus_evtflags = evtflags; 845301583Ssephe 846301583Ssephe sc->vmbus_mnf1 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, 847301583Ssephe PAGE_SIZE, &sc->vmbus_mnf1_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); 848301583Ssephe if (sc->vmbus_mnf1 == NULL) 849301583Ssephe return ENOMEM; 850301583Ssephe 851301583Ssephe sc->vmbus_mnf2 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, 852307310Ssephe sizeof(struct vmbus_mnf), &sc->vmbus_mnf2_dma, 853307310Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 854301583Ssephe if (sc->vmbus_mnf2 == NULL) 855301583Ssephe return ENOMEM; 856301583Ssephe 857300644Ssephe return 0; 858300572Ssephe} 859300572Ssephe 860300572Ssephestatic void 861300572Ssephevmbus_dma_free(struct vmbus_softc *sc) 862300572Ssephe{ 863300572Ssephe int cpu; 864300572Ssephe 865301583Ssephe if (sc->vmbus_evtflags != NULL) { 866301583Ssephe hyperv_dmamem_free(&sc->vmbus_evtflags_dma, sc->vmbus_evtflags); 867301583Ssephe sc->vmbus_evtflags = NULL; 868301583Ssephe sc->vmbus_rx_evtflags = NULL; 869301583Ssephe sc->vmbus_tx_evtflags = NULL; 870301583Ssephe } 871301583Ssephe if (sc->vmbus_mnf1 != NULL) { 872301583Ssephe hyperv_dmamem_free(&sc->vmbus_mnf1_dma, sc->vmbus_mnf1); 873301583Ssephe sc->vmbus_mnf1 = NULL; 874301583Ssephe } 875301583Ssephe if (sc->vmbus_mnf2 != NULL) { 876301583Ssephe hyperv_dmamem_free(&sc->vmbus_mnf2_dma, sc->vmbus_mnf2); 877301583Ssephe sc->vmbus_mnf2 = NULL; 878301583Ssephe } 879301583Ssephe 880300572Ssephe CPU_FOREACH(cpu) { 881300573Ssephe if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) { 882300572Ssephe hyperv_dmamem_free( 883300573Ssephe VMBUS_PCPU_PTR(sc, message_dma, cpu), 884300573Ssephe VMBUS_PCPU_GET(sc, message, cpu)); 885300573Ssephe VMBUS_PCPU_GET(sc, message, cpu) = NULL; 886300572Ssephe } 887301106Ssephe if (VMBUS_PCPU_GET(sc, event_flags, cpu) != NULL) { 888300572Ssephe hyperv_dmamem_free( 889301106Ssephe VMBUS_PCPU_PTR(sc, event_flags_dma, cpu), 890301106Ssephe VMBUS_PCPU_GET(sc, event_flags, cpu)); 891301106Ssephe VMBUS_PCPU_GET(sc, event_flags, cpu) = NULL; 892300572Ssephe } 893300572Ssephe } 894300572Ssephe} 895300572Ssephe 896250199Sgrehanstatic int 897300574Ssephevmbus_intr_setup(struct vmbus_softc *sc) 898300574Ssephe{ 899300574Ssephe int cpu; 900300574Ssephe 901300574Ssephe CPU_FOREACH(cpu) { 902300574Ssephe char buf[MAXCOMLEN + 1]; 903300645Ssephe cpuset_t cpu_mask; 904300574Ssephe 905300645Ssephe /* Allocate an interrupt counter for Hyper-V interrupt */ 906300574Ssephe snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu); 907300574Ssephe intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu)); 908300574Ssephe 909300574Ssephe /* 910300645Ssephe * Setup taskqueue to handle events. Task will be per- 911300645Ssephe * channel. 912300574Ssephe */ 913300646Ssephe VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast( 914300646Ssephe "hyperv event", M_WAITOK, taskqueue_thread_enqueue, 915300646Ssephe VMBUS_PCPU_PTR(sc, event_tq, cpu)); 916324576Ssephe if (vmbus_pin_evttask) { 917324576Ssephe CPU_SETOF(cpu, &cpu_mask); 918324576Ssephe taskqueue_start_threads_cpuset( 919324576Ssephe VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, 920324576Ssephe &cpu_mask, "hvevent%d", cpu); 921324576Ssephe } else { 922324576Ssephe taskqueue_start_threads( 923324576Ssephe VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, 924324576Ssephe "hvevent%d", cpu); 925324576Ssephe } 926300574Ssephe 927300574Ssephe /* 928300645Ssephe * Setup tasks and taskqueues to handle messages. 929300574Ssephe */ 930300646Ssephe VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast( 931300574Ssephe "hyperv msg", M_WAITOK, taskqueue_thread_enqueue, 932300646Ssephe VMBUS_PCPU_PTR(sc, message_tq, cpu)); 933300574Ssephe CPU_SETOF(cpu, &cpu_mask); 934300574Ssephe taskqueue_start_threads_cpuset( 935300646Ssephe VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask, 936300646Ssephe "hvmsg%d", cpu); 937300646Ssephe TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0, 938300574Ssephe vmbus_msg_task, sc); 939300574Ssephe } 940300645Ssephe 941300645Ssephe /* 942300645Ssephe * All Hyper-V ISR required resources are setup, now let's find a 943300645Ssephe * free IDT vector for Hyper-V ISR and set it up. 944300645Ssephe */ 945329462Skib sc->vmbus_idtvec = lapic_ipi_alloc(pti ? IDTVEC(vmbus_isr_pti) : 946329462Skib IDTVEC(vmbus_isr)); 947300645Ssephe if (sc->vmbus_idtvec < 0) { 948300645Ssephe device_printf(sc->vmbus_dev, "cannot find free IDT vector\n"); 949300645Ssephe return ENXIO; 950300645Ssephe } 951308621Ssephe if (bootverbose) { 952300645Ssephe device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n", 953300645Ssephe sc->vmbus_idtvec); 954300645Ssephe } 955300574Ssephe return 0; 956300574Ssephe} 957300574Ssephe 958300574Ssephestatic void 959300574Ssephevmbus_intr_teardown(struct vmbus_softc *sc) 960300574Ssephe{ 961300574Ssephe int cpu; 962300574Ssephe 963300645Ssephe if (sc->vmbus_idtvec >= 0) { 964300645Ssephe lapic_ipi_free(sc->vmbus_idtvec); 965300645Ssephe sc->vmbus_idtvec = -1; 966300645Ssephe } 967300645Ssephe 968300574Ssephe CPU_FOREACH(cpu) { 969300646Ssephe if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) { 970300646Ssephe taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu)); 971300646Ssephe VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL; 972300574Ssephe } 973300646Ssephe if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) { 974300646Ssephe taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu), 975300646Ssephe VMBUS_PCPU_PTR(sc, message_task, cpu)); 976300646Ssephe taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu)); 977300646Ssephe VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL; 978300576Ssephe } 979300574Ssephe } 980300574Ssephe} 981300574Ssephe 982300574Ssephestatic int 983300651Ssephevmbus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 984250199Sgrehan{ 985250199Sgrehan return (ENOENT); 986250199Sgrehan} 987250199Sgrehan 988250199Sgrehanstatic int 989297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) 990297143Ssephe{ 991307461Ssephe const struct vmbus_channel *chan; 992301021Ssephe char guidbuf[HYPERV_GUID_STRLEN]; 993297143Ssephe 994307307Ssephe chan = vmbus_get_channel(child); 995307307Ssephe if (chan == NULL) { 996307307Ssephe /* Event timer device, which does not belong to a channel */ 997298449Ssephe return (0); 998307307Ssephe } 999298449Ssephe 1000297143Ssephe strlcat(buf, "classid=", buflen); 1001307307Ssephe hyperv_guid2str(&chan->ch_guid_type, guidbuf, sizeof(guidbuf)); 1002297143Ssephe strlcat(buf, guidbuf, buflen); 1003297143Ssephe 1004297143Ssephe strlcat(buf, " deviceid=", buflen); 1005307307Ssephe hyperv_guid2str(&chan->ch_guid_inst, guidbuf, sizeof(guidbuf)); 1006297143Ssephe strlcat(buf, guidbuf, buflen); 1007297143Ssephe 1008297143Ssephe return (0); 1009297143Ssephe} 1010297143Ssephe 1011307307Ssepheint 1012307461Ssephevmbus_add_child(struct vmbus_channel *chan) 1013250199Sgrehan{ 1014307461Ssephe struct vmbus_softc *sc = chan->ch_vmbus; 1015307307Ssephe device_t parent = sc->vmbus_dev; 1016250199Sgrehan 1017307599Ssephe mtx_lock(&Giant); 1018307449Ssephe 1019307307Ssephe chan->ch_dev = device_add_child(parent, NULL, -1); 1020307307Ssephe if (chan->ch_dev == NULL) { 1021307599Ssephe mtx_unlock(&Giant); 1022307307Ssephe device_printf(parent, "device_add_child for chan%u failed\n", 1023307307Ssephe chan->ch_id); 1024307599Ssephe return (ENXIO); 1025307307Ssephe } 1026307307Ssephe device_set_ivars(chan->ch_dev, chan); 1027307599Ssephe device_probe_and_attach(chan->ch_dev); 1028250199Sgrehan 1029307599Ssephe mtx_unlock(&Giant); 1030307599Ssephe return (0); 1031250199Sgrehan} 1032250199Sgrehan 1033307307Ssepheint 1034307461Ssephevmbus_delete_child(struct vmbus_channel *chan) 1035250199Sgrehan{ 1036307599Ssephe int error = 0; 1037250199Sgrehan 1038307599Ssephe mtx_lock(&Giant); 1039307599Ssephe if (chan->ch_dev != NULL) { 1040307599Ssephe error = device_delete_child(chan->ch_vmbus->vmbus_dev, 1041307599Ssephe chan->ch_dev); 1042308621Ssephe chan->ch_dev = NULL; 1043297142Ssephe } 1044250199Sgrehan mtx_unlock(&Giant); 1045307599Ssephe return (error); 1046250199Sgrehan} 1047250199Sgrehan 1048250199Sgrehanstatic int 1049307291Ssephevmbus_sysctl_version(SYSCTL_HANDLER_ARGS) 1050307291Ssephe{ 1051307302Ssephe struct vmbus_softc *sc = arg1; 1052307291Ssephe char verstr[16]; 1053307291Ssephe 1054307291Ssephe snprintf(verstr, sizeof(verstr), "%u.%u", 1055307302Ssephe VMBUS_VERSION_MAJOR(sc->vmbus_version), 1056307302Ssephe VMBUS_VERSION_MINOR(sc->vmbus_version)); 1057307291Ssephe return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); 1058307291Ssephe} 1059307291Ssephe 1060309312Sdexuan/* 1061309312Sdexuan * We need the function to make sure the MMIO resource is allocated from the 1062309312Sdexuan * ranges found in _CRS. 1063309312Sdexuan * 1064309312Sdexuan * For the release function, we can use bus_generic_release_resource(). 1065309312Sdexuan */ 1066309312Sdexuanstatic struct resource * 1067309312Sdexuanvmbus_alloc_resource(device_t dev, device_t child, int type, int *rid, 1068309312Sdexuan rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 1069309312Sdexuan{ 1070309312Sdexuan device_t parent = device_get_parent(dev); 1071309312Sdexuan struct resource *res; 1072309312Sdexuan 1073309312Sdexuan#ifdef NEW_PCIB 1074309312Sdexuan if (type == SYS_RES_MEMORY) { 1075309312Sdexuan struct vmbus_softc *sc = device_get_softc(dev); 1076309312Sdexuan 1077309312Sdexuan res = pcib_host_res_alloc(&sc->vmbus_mmio_res, child, type, 1078309312Sdexuan rid, start, end, count, flags); 1079309312Sdexuan } else 1080309312Sdexuan#endif 1081309312Sdexuan { 1082309312Sdexuan res = BUS_ALLOC_RESOURCE(parent, child, type, rid, start, 1083309312Sdexuan end, count, flags); 1084309312Sdexuan } 1085309312Sdexuan 1086309312Sdexuan return (res); 1087309312Sdexuan} 1088309312Sdexuan 1089309312Sdexuanstatic int 1090309312Sdexuanvmbus_alloc_msi(device_t bus, device_t dev, int count, int maxcount, int *irqs) 1091309312Sdexuan{ 1092318392Ssephe 1093318392Ssephe return (PCIB_ALLOC_MSI(device_get_parent(bus), dev, count, maxcount, 1094318392Ssephe irqs)); 1095309312Sdexuan} 1096309312Sdexuan 1097309312Sdexuanstatic int 1098309312Sdexuanvmbus_release_msi(device_t bus, device_t dev, int count, int *irqs) 1099309312Sdexuan{ 1100318392Ssephe 1101318392Ssephe return (PCIB_RELEASE_MSI(device_get_parent(bus), dev, count, irqs)); 1102309312Sdexuan} 1103309312Sdexuan 1104309312Sdexuanstatic int 1105309312Sdexuanvmbus_alloc_msix(device_t bus, device_t dev, int *irq) 1106309312Sdexuan{ 1107318392Ssephe 1108318392Ssephe return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq)); 1109309312Sdexuan} 1110309312Sdexuan 1111309312Sdexuanstatic int 1112309312Sdexuanvmbus_release_msix(device_t bus, device_t dev, int irq) 1113309312Sdexuan{ 1114318392Ssephe 1115318392Ssephe return (PCIB_RELEASE_MSIX(device_get_parent(bus), dev, irq)); 1116309312Sdexuan} 1117309312Sdexuan 1118309312Sdexuanstatic int 1119309312Sdexuanvmbus_map_msi(device_t bus, device_t dev, int irq, uint64_t *addr, 1120309312Sdexuan uint32_t *data) 1121309312Sdexuan{ 1122318392Ssephe 1123318392Ssephe return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data)); 1124309312Sdexuan} 1125309312Sdexuan 1126307302Ssephestatic uint32_t 1127307302Ssephevmbus_get_version_method(device_t bus, device_t dev) 1128307302Ssephe{ 1129307302Ssephe struct vmbus_softc *sc = device_get_softc(bus); 1130307302Ssephe 1131307302Ssephe return sc->vmbus_version; 1132307302Ssephe} 1133307302Ssephe 1134307291Ssephestatic int 1135307448Ssephevmbus_probe_guid_method(device_t bus, device_t dev, 1136307448Ssephe const struct hyperv_guid *guid) 1137307307Ssephe{ 1138307461Ssephe const struct vmbus_channel *chan = vmbus_get_channel(dev); 1139307307Ssephe 1140307448Ssephe if (memcmp(&chan->ch_guid_type, guid, sizeof(struct hyperv_guid)) == 0) 1141307307Ssephe return 0; 1142307307Ssephe return ENXIO; 1143307307Ssephe} 1144307307Ssephe 1145309312Sdexuanstatic uint32_t 1146309312Sdexuanvmbus_get_vcpu_id_method(device_t bus, device_t dev, int cpu) 1147309312Sdexuan{ 1148309312Sdexuan const struct vmbus_softc *sc = device_get_softc(bus); 1149309312Sdexuan 1150309312Sdexuan return (VMBUS_PCPU_GET(sc, vcpuid, cpu)); 1151309312Sdexuan} 1152309312Sdexuan 1153311368Ssephestatic struct taskqueue * 1154311368Ssephevmbus_get_eventtq_method(device_t bus, device_t dev __unused, int cpu) 1155311368Ssephe{ 1156311368Ssephe const struct vmbus_softc *sc = device_get_softc(bus); 1157311368Ssephe 1158311368Ssephe KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu%d", cpu)); 1159311368Ssephe return (VMBUS_PCPU_GET(sc, event_tq, cpu)); 1160311368Ssephe} 1161311368Ssephe 1162309312Sdexuan#ifdef NEW_PCIB 1163309312Sdexuan#define VTPM_BASE_ADDR 0xfed40000 1164309312Sdexuan#define FOUR_GB (1ULL << 32) 1165309312Sdexuan 1166309312Sdexuanenum parse_pass { parse_64, parse_32 }; 1167309312Sdexuan 1168309312Sdexuanstruct parse_context { 1169309312Sdexuan device_t vmbus_dev; 1170309312Sdexuan enum parse_pass pass; 1171309312Sdexuan}; 1172309312Sdexuan 1173309312Sdexuanstatic ACPI_STATUS 1174309312Sdexuanparse_crs(ACPI_RESOURCE *res, void *ctx) 1175309312Sdexuan{ 1176309312Sdexuan const struct parse_context *pc = ctx; 1177309312Sdexuan device_t vmbus_dev = pc->vmbus_dev; 1178309312Sdexuan 1179309312Sdexuan struct vmbus_softc *sc = device_get_softc(vmbus_dev); 1180309312Sdexuan UINT64 start, end; 1181309312Sdexuan 1182309312Sdexuan switch (res->Type) { 1183309312Sdexuan case ACPI_RESOURCE_TYPE_ADDRESS32: 1184309312Sdexuan start = res->Data.Address32.Address.Minimum; 1185309312Sdexuan end = res->Data.Address32.Address.Maximum; 1186309312Sdexuan break; 1187309312Sdexuan 1188309312Sdexuan case ACPI_RESOURCE_TYPE_ADDRESS64: 1189309312Sdexuan start = res->Data.Address64.Address.Minimum; 1190309312Sdexuan end = res->Data.Address64.Address.Maximum; 1191309312Sdexuan break; 1192309312Sdexuan 1193309312Sdexuan default: 1194309312Sdexuan /* Unused types. */ 1195309312Sdexuan return (AE_OK); 1196309312Sdexuan } 1197309312Sdexuan 1198309312Sdexuan /* 1199309312Sdexuan * We don't use <1MB addresses. 1200309312Sdexuan */ 1201309312Sdexuan if (end < 0x100000) 1202309312Sdexuan return (AE_OK); 1203309312Sdexuan 1204309312Sdexuan /* Don't conflict with vTPM. */ 1205309312Sdexuan if (end >= VTPM_BASE_ADDR && start < VTPM_BASE_ADDR) 1206309312Sdexuan end = VTPM_BASE_ADDR - 1; 1207309312Sdexuan 1208309312Sdexuan if ((pc->pass == parse_32 && start < FOUR_GB) || 1209309312Sdexuan (pc->pass == parse_64 && start >= FOUR_GB)) 1210309312Sdexuan pcib_host_res_decodes(&sc->vmbus_mmio_res, SYS_RES_MEMORY, 1211309312Sdexuan start, end, 0); 1212309312Sdexuan 1213309312Sdexuan return (AE_OK); 1214309312Sdexuan} 1215309312Sdexuan 1216309312Sdexuanstatic void 1217309312Sdexuanvmbus_get_crs(device_t dev, device_t vmbus_dev, enum parse_pass pass) 1218309312Sdexuan{ 1219309312Sdexuan struct parse_context pc; 1220309312Sdexuan ACPI_STATUS status; 1221309312Sdexuan 1222309312Sdexuan if (bootverbose) 1223309312Sdexuan device_printf(dev, "walking _CRS, pass=%d\n", pass); 1224309312Sdexuan 1225309312Sdexuan pc.vmbus_dev = vmbus_dev; 1226309312Sdexuan pc.pass = pass; 1227309312Sdexuan status = AcpiWalkResources(acpi_get_handle(dev), "_CRS", 1228309312Sdexuan parse_crs, &pc); 1229309312Sdexuan 1230309312Sdexuan if (bootverbose && ACPI_FAILURE(status)) 1231309312Sdexuan device_printf(dev, "_CRS: not found, pass=%d\n", pass); 1232309312Sdexuan} 1233309312Sdexuan 1234309312Sdexuanstatic void 1235309312Sdexuanvmbus_get_mmio_res_pass(device_t dev, enum parse_pass pass) 1236309312Sdexuan{ 1237318392Ssephe device_t acpi0, parent; 1238309312Sdexuan 1239318392Ssephe parent = device_get_parent(dev); 1240309312Sdexuan 1241318392Ssephe acpi0 = device_get_parent(parent); 1242318392Ssephe if (strcmp("acpi0", device_get_nameunit(acpi0)) == 0) { 1243318392Ssephe device_t *children; 1244318392Ssephe int count; 1245309312Sdexuan 1246318392Ssephe /* 1247318392Ssephe * Try to locate VMBUS resources and find _CRS on them. 1248318392Ssephe */ 1249318392Ssephe if (device_get_children(acpi0, &children, &count) == 0) { 1250318392Ssephe int i; 1251309312Sdexuan 1252318392Ssephe for (i = 0; i < count; ++i) { 1253318392Ssephe if (!device_is_attached(children[i])) 1254318392Ssephe continue; 1255309312Sdexuan 1256318392Ssephe if (strcmp("vmbus_res", 1257318392Ssephe device_get_name(children[i])) == 0) 1258318392Ssephe vmbus_get_crs(children[i], dev, pass); 1259318392Ssephe } 1260318392Ssephe free(children, M_TEMP); 1261318392Ssephe } 1262309312Sdexuan 1263318392Ssephe /* 1264318392Ssephe * Try to find _CRS on acpi. 1265318392Ssephe */ 1266318392Ssephe vmbus_get_crs(acpi0, dev, pass); 1267318392Ssephe } else { 1268318392Ssephe device_printf(dev, "not grandchild of acpi\n"); 1269309312Sdexuan } 1270309312Sdexuan 1271318392Ssephe /* 1272318392Ssephe * Try to find _CRS on parent. 1273318392Ssephe */ 1274318392Ssephe vmbus_get_crs(parent, dev, pass); 1275309312Sdexuan} 1276309312Sdexuan 1277309312Sdexuanstatic void 1278309312Sdexuanvmbus_get_mmio_res(device_t dev) 1279309312Sdexuan{ 1280309312Sdexuan struct vmbus_softc *sc = device_get_softc(dev); 1281309312Sdexuan /* 1282309312Sdexuan * We walk the resources twice to make sure that: in the resource 1283309312Sdexuan * list, the 32-bit resources appear behind the 64-bit resources. 1284309312Sdexuan * NB: resource_list_add() uses INSERT_TAIL. This way, when we 1285309312Sdexuan * iterate through the list to find a range for a 64-bit BAR in 1286309312Sdexuan * vmbus_alloc_resource(), we can make sure we try to use >4GB 1287309312Sdexuan * ranges first. 1288309312Sdexuan */ 1289309312Sdexuan pcib_host_res_init(dev, &sc->vmbus_mmio_res); 1290309312Sdexuan 1291309312Sdexuan vmbus_get_mmio_res_pass(dev, parse_64); 1292309312Sdexuan vmbus_get_mmio_res_pass(dev, parse_32); 1293309312Sdexuan} 1294309312Sdexuan 1295309312Sdexuanstatic void 1296309312Sdexuanvmbus_free_mmio_res(device_t dev) 1297309312Sdexuan{ 1298309312Sdexuan struct vmbus_softc *sc = device_get_softc(dev); 1299309312Sdexuan 1300309312Sdexuan pcib_host_res_free(dev, &sc->vmbus_mmio_res); 1301309312Sdexuan} 1302309312Sdexuan#endif /* NEW_PCIB */ 1303309312Sdexuan 1304318392Ssephestatic void 1305318392Ssephevmbus_identify(driver_t *driver, device_t parent) 1306318392Ssephe{ 1307318392Ssephe 1308318392Ssephe if (device_get_unit(parent) != 0 || vm_guest != VM_GUEST_HV || 1309318392Ssephe (hyperv_features & CPUID_HV_MSR_SYNIC) == 0) 1310318392Ssephe return; 1311318392Ssephe device_add_child(parent, "vmbus", -1); 1312318392Ssephe} 1313318392Ssephe 1314307307Ssephestatic int 1315300127Ssephevmbus_probe(device_t dev) 1316300127Ssephe{ 1317301018Ssephe 1318318392Ssephe if (device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV || 1319300834Ssephe (hyperv_features & CPUID_HV_MSR_SYNIC) == 0) 1320293870Ssephe return (ENXIO); 1321250199Sgrehan 1322300129Ssephe device_set_desc(dev, "Hyper-V Vmbus"); 1323293870Ssephe return (BUS_PROBE_DEFAULT); 1324250199Sgrehan} 1325250199Sgrehan 1326282212Swhu/** 1327250199Sgrehan * @brief Main vmbus driver initialization routine. 1328250199Sgrehan * 1329250199Sgrehan * Here, we 1330250199Sgrehan * - initialize the vmbus driver context 1331250199Sgrehan * - setup various driver entry points 1332250199Sgrehan * - invoke the vmbus hv main init routine 1333250199Sgrehan * - get the irq resource 1334250199Sgrehan * - invoke the vmbus to add the vmbus root device 1335250199Sgrehan * - setup the vmbus root device 1336250199Sgrehan * - retrieve the channel offers 1337250199Sgrehan */ 1338250199Sgrehanstatic int 1339307291Ssephevmbus_doattach(struct vmbus_softc *sc) 1340250199Sgrehan{ 1341307291Ssephe struct sysctl_oid_list *child; 1342307291Ssephe struct sysctl_ctx_list *ctx; 1343300574Ssephe int ret; 1344250199Sgrehan 1345300650Ssephe if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED) 1346250199Sgrehan return (0); 1347309312Sdexuan 1348309312Sdexuan#ifdef NEW_PCIB 1349309312Sdexuan vmbus_get_mmio_res(sc->vmbus_dev); 1350309312Sdexuan#endif 1351309312Sdexuan 1352300650Ssephe sc->vmbus_flags |= VMBUS_FLAG_ATTACHED; 1353250199Sgrehan 1354307302Ssephe sc->vmbus_gpadl = VMBUS_GPADL_START; 1355307450Ssephe mtx_init(&sc->vmbus_prichan_lock, "vmbus prichan", NULL, MTX_DEF); 1356307450Ssephe TAILQ_INIT(&sc->vmbus_prichans); 1357307599Ssephe mtx_init(&sc->vmbus_chan_lock, "vmbus channel", NULL, MTX_DEF); 1358307599Ssephe TAILQ_INIT(&sc->vmbus_chans); 1359307304Ssephe sc->vmbus_chmap = malloc( 1360307461Ssephe sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX, M_DEVBUF, 1361307304Ssephe M_WAITOK | M_ZERO); 1362307291Ssephe 1363250199Sgrehan /* 1364307278Ssephe * Create context for "post message" Hypercalls 1365307278Ssephe */ 1366307475Ssephe sc->vmbus_xc = vmbus_xact_ctx_create(bus_get_dma_tag(sc->vmbus_dev), 1367307475Ssephe HYPERCALL_POSTMSGIN_SIZE, VMBUS_MSG_SIZE, 1368307475Ssephe sizeof(struct vmbus_msghc)); 1369307475Ssephe if (sc->vmbus_xc == NULL) { 1370307278Ssephe ret = ENXIO; 1371307278Ssephe goto cleanup; 1372307278Ssephe } 1373307278Ssephe 1374307278Ssephe /* 1375300645Ssephe * Allocate DMA stuffs. 1376250199Sgrehan */ 1377300645Ssephe ret = vmbus_dma_alloc(sc); 1378300574Ssephe if (ret != 0) 1379282212Swhu goto cleanup; 1380250199Sgrehan 1381250199Sgrehan /* 1382300645Ssephe * Setup interrupt. 1383250199Sgrehan */ 1384300645Ssephe ret = vmbus_intr_setup(sc); 1385300644Ssephe if (ret != 0) 1386300644Ssephe goto cleanup; 1387300572Ssephe 1388300650Ssephe /* 1389300650Ssephe * Setup SynIC. 1390300650Ssephe */ 1391282212Swhu if (bootverbose) 1392300650Ssephe device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started); 1393300652Ssephe smp_rendezvous(NULL, vmbus_synic_setup, NULL, sc); 1394300650Ssephe sc->vmbus_flags |= VMBUS_FLAG_SYNIC; 1395282212Swhu 1396255414Sgrehan /* 1397307304Ssephe * Initialize vmbus, e.g. connect to Hypervisor. 1398250199Sgrehan */ 1399307278Ssephe ret = vmbus_init(sc); 1400307278Ssephe if (ret != 0) 1401307278Ssephe goto cleanup; 1402307278Ssephe 1403307302Ssephe if (sc->vmbus_version == VMBUS_VERSION_WS2008 || 1404307302Ssephe sc->vmbus_version == VMBUS_VERSION_WIN7) 1405300107Ssephe sc->vmbus_event_proc = vmbus_event_proc_compat; 1406300107Ssephe else 1407300107Ssephe sc->vmbus_event_proc = vmbus_event_proc; 1408300107Ssephe 1409307291Ssephe ret = vmbus_scan(sc); 1410307291Ssephe if (ret != 0) 1411307291Ssephe goto cleanup; 1412298260Ssephe 1413307291Ssephe ctx = device_get_sysctl_ctx(sc->vmbus_dev); 1414307291Ssephe child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->vmbus_dev)); 1415307291Ssephe SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "version", 1416307302Ssephe CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 1417307291Ssephe vmbus_sysctl_version, "A", "vmbus version"); 1418298260Ssephe 1419250199Sgrehan return (ret); 1420250199Sgrehan 1421300574Ssephecleanup: 1422307599Ssephe vmbus_scan_teardown(sc); 1423300645Ssephe vmbus_intr_teardown(sc); 1424300572Ssephe vmbus_dma_free(sc); 1425307475Ssephe if (sc->vmbus_xc != NULL) { 1426307475Ssephe vmbus_xact_ctx_destroy(sc->vmbus_xc); 1427307475Ssephe sc->vmbus_xc = NULL; 1428307278Ssephe } 1429311389Ssephe free(__DEVOLATILE(void *, sc->vmbus_chmap), M_DEVBUF); 1430307450Ssephe mtx_destroy(&sc->vmbus_prichan_lock); 1431307599Ssephe mtx_destroy(&sc->vmbus_chan_lock); 1432250199Sgrehan 1433250199Sgrehan return (ret); 1434250199Sgrehan} 1435250199Sgrehan 1436300107Ssephestatic void 1437300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused) 1438300107Ssephe{ 1439300107Ssephe} 1440300107Ssephe 1441310573Ssephe#ifdef EARLY_AP_STARTUP 1442310573Ssephe 1443310573Ssephestatic void 1444310573Ssephevmbus_intrhook(void *xsc) 1445310573Ssephe{ 1446310573Ssephe struct vmbus_softc *sc = xsc; 1447310573Ssephe 1448310573Ssephe if (bootverbose) 1449310573Ssephe device_printf(sc->vmbus_dev, "intrhook\n"); 1450310573Ssephe vmbus_doattach(sc); 1451310573Ssephe config_intrhook_disestablish(&sc->vmbus_intrhook); 1452310573Ssephe} 1453310573Ssephe 1454310573Ssephe#endif /* EARLY_AP_STARTUP */ 1455310573Ssephe 1456250199Sgrehanstatic int 1457250199Sgrehanvmbus_attach(device_t dev) 1458250199Sgrehan{ 1459300102Ssephe vmbus_sc = device_get_softc(dev); 1460300486Ssephe vmbus_sc->vmbus_dev = dev; 1461300574Ssephe vmbus_sc->vmbus_idtvec = -1; 1462250199Sgrehan 1463300107Ssephe /* 1464300107Ssephe * Event processing logic will be configured: 1465300107Ssephe * - After the vmbus protocol version negotiation. 1466300107Ssephe * - Before we request channel offers. 1467300107Ssephe */ 1468300107Ssephe vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy; 1469300107Ssephe 1470310573Ssephe#ifdef EARLY_AP_STARTUP 1471310573Ssephe /* 1472310573Ssephe * Defer the real attach until the pause(9) works as expected. 1473310573Ssephe */ 1474310573Ssephe vmbus_sc->vmbus_intrhook.ich_func = vmbus_intrhook; 1475310573Ssephe vmbus_sc->vmbus_intrhook.ich_arg = vmbus_sc; 1476310573Ssephe config_intrhook_establish(&vmbus_sc->vmbus_intrhook); 1477310573Ssephe#else /* !EARLY_AP_STARTUP */ 1478250199Sgrehan /* 1479250199Sgrehan * If the system has already booted and thread 1480250199Sgrehan * scheduling is possible indicated by the global 1481250199Sgrehan * cold set to zero, we just call the driver 1482250199Sgrehan * initialization directly. 1483250199Sgrehan */ 1484250199Sgrehan if (!cold) 1485307291Ssephe vmbus_doattach(vmbus_sc); 1486310573Ssephe#endif /* EARLY_AP_STARTUP */ 1487250199Sgrehan 1488250199Sgrehan return (0); 1489250199Sgrehan} 1490250199Sgrehan 1491300121Ssephestatic int 1492300121Ssephevmbus_detach(device_t dev) 1493250199Sgrehan{ 1494300487Ssephe struct vmbus_softc *sc = device_get_softc(dev); 1495255414Sgrehan 1496307599Ssephe bus_generic_detach(dev); 1497307450Ssephe vmbus_chan_destroy_all(sc); 1498307291Ssephe 1499307599Ssephe vmbus_scan_teardown(sc); 1500307599Ssephe 1501307302Ssephe vmbus_disconnect(sc); 1502250199Sgrehan 1503300650Ssephe if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) { 1504300650Ssephe sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC; 1505300650Ssephe smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL); 1506300650Ssephe } 1507250199Sgrehan 1508300645Ssephe vmbus_intr_teardown(sc); 1509300572Ssephe vmbus_dma_free(sc); 1510255414Sgrehan 1511307475Ssephe if (sc->vmbus_xc != NULL) { 1512307475Ssephe vmbus_xact_ctx_destroy(sc->vmbus_xc); 1513307475Ssephe sc->vmbus_xc = NULL; 1514307278Ssephe } 1515307278Ssephe 1516311389Ssephe free(__DEVOLATILE(void *, sc->vmbus_chmap), M_DEVBUF); 1517307450Ssephe mtx_destroy(&sc->vmbus_prichan_lock); 1518307599Ssephe mtx_destroy(&sc->vmbus_chan_lock); 1519307304Ssephe 1520309312Sdexuan#ifdef NEW_PCIB 1521309312Sdexuan vmbus_free_mmio_res(dev); 1522309312Sdexuan#endif 1523309312Sdexuan 1524250199Sgrehan return (0); 1525250199Sgrehan} 1526250199Sgrehan 1527307466Ssephe#ifndef EARLY_AP_STARTUP 1528250199Sgrehan 1529307466Ssephestatic void 1530307466Ssephevmbus_sysinit(void *arg __unused) 1531307466Ssephe{ 1532307466Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 1533250199Sgrehan 1534307466Ssephe if (vm_guest != VM_GUEST_HV || sc == NULL) 1535307466Ssephe return; 1536307302Ssephe 1537307466Ssephe /* 1538307466Ssephe * If the system has already booted and thread 1539307466Ssephe * scheduling is possible, as indicated by the 1540307466Ssephe * global cold set to zero, we just call the driver 1541307466Ssephe * initialization directly. 1542307466Ssephe */ 1543307466Ssephe if (!cold) 1544307466Ssephe vmbus_doattach(sc); 1545307466Ssephe} 1546300126Ssephe/* 1547300126Ssephe * NOTE: 1548300126Ssephe * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is 1549300126Ssephe * initialized. 1550300126Ssephe */ 1551300126SsepheSYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL); 1552307466Ssephe 1553307466Ssephe#endif /* !EARLY_AP_STARTUP */ 1554