vmbus.c revision 318392
1250199Sgrehan/*- 2298446Ssephe * Copyright (c) 2009-2012,2016 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 318392 2017-05-17 01:48:44Z sephe $"); 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> 49309312Sdexuan#include <machine/resource.h> 50307466Ssephe#include <x86/include/apicvar.h> 51250199Sgrehan 52307466Ssephe#include <contrib/dev/acpica/include/acpi.h> 53309312Sdexuan#include <dev/acpica/acpivar.h> 54307466Ssephe 55297142Ssephe#include <dev/hyperv/include/hyperv.h> 56307475Ssephe#include <dev/hyperv/include/vmbus_xact.h> 57300654Ssephe#include <dev/hyperv/vmbus/hyperv_reg.h> 58300834Ssephe#include <dev/hyperv/vmbus/hyperv_var.h> 59301019Ssephe#include <dev/hyperv/vmbus/vmbus_reg.h> 60300102Ssephe#include <dev/hyperv/vmbus/vmbus_var.h> 61307463Ssephe#include <dev/hyperv/vmbus/vmbus_chanvar.h> 62250199Sgrehan 63293870Ssephe#include "acpi_if.h" 64309312Sdexuan#include "pcib_if.h" 65307302Ssephe#include "vmbus_if.h" 66250199Sgrehan 67307302Ssephe#define VMBUS_GPADL_START 0xe1e10 68307278Ssephe 69307278Ssephestruct vmbus_msghc { 70307475Ssephe struct vmbus_xact *mh_xact; 71307278Ssephe struct hypercall_postmsg_in mh_inprm_save; 72307278Ssephe}; 73307278Ssephe 74318392Ssephestatic void vmbus_identify(driver_t *, device_t); 75307466Ssephestatic int vmbus_probe(device_t); 76307466Ssephestatic int vmbus_attach(device_t); 77307466Ssephestatic int vmbus_detach(device_t); 78307466Ssephestatic int vmbus_read_ivar(device_t, device_t, int, 79307466Ssephe uintptr_t *); 80307466Ssephestatic int vmbus_child_pnpinfo_str(device_t, device_t, 81307466Ssephe char *, size_t); 82309312Sdexuanstatic struct resource *vmbus_alloc_resource(device_t dev, 83309312Sdexuan device_t child, int type, int *rid, 84309312Sdexuan rman_res_t start, rman_res_t end, 85309312Sdexuan rman_res_t count, u_int flags); 86309312Sdexuanstatic int vmbus_alloc_msi(device_t bus, device_t dev, 87309312Sdexuan int count, int maxcount, int *irqs); 88309312Sdexuanstatic int vmbus_release_msi(device_t bus, device_t dev, 89309312Sdexuan int count, int *irqs); 90309312Sdexuanstatic int vmbus_alloc_msix(device_t bus, device_t dev, 91309312Sdexuan int *irq); 92309312Sdexuanstatic int vmbus_release_msix(device_t bus, device_t dev, 93309312Sdexuan int irq); 94309312Sdexuanstatic int vmbus_map_msi(device_t bus, device_t dev, 95309312Sdexuan int irq, uint64_t *addr, uint32_t *data); 96307466Ssephestatic uint32_t vmbus_get_version_method(device_t, device_t); 97307466Ssephestatic int vmbus_probe_guid_method(device_t, device_t, 98307466Ssephe const struct hyperv_guid *); 99309312Sdexuanstatic uint32_t vmbus_get_vcpu_id_method(device_t bus, 100309312Sdexuan device_t dev, int cpu); 101311368Ssephestatic struct taskqueue *vmbus_get_eventtq_method(device_t, device_t, 102311368Ssephe int); 103310573Ssephe#ifdef EARLY_AP_STARTUP 104310573Ssephestatic void vmbus_intrhook(void *); 105310573Ssephe#endif 106307466Ssephe 107307278Ssephestatic int vmbus_init(struct vmbus_softc *); 108307302Ssephestatic int vmbus_connect(struct vmbus_softc *, uint32_t); 109307291Ssephestatic int vmbus_req_channels(struct vmbus_softc *sc); 110307302Ssephestatic void vmbus_disconnect(struct vmbus_softc *); 111307291Ssephestatic int vmbus_scan(struct vmbus_softc *); 112307599Ssephestatic void vmbus_scan_teardown(struct vmbus_softc *); 113307451Ssephestatic void vmbus_scan_done(struct vmbus_softc *, 114307451Ssephe const struct vmbus_message *); 115307451Ssephestatic void vmbus_chanmsg_handle(struct vmbus_softc *, 116307451Ssephe const struct vmbus_message *); 117307466Ssephestatic void vmbus_msg_task(void *, int); 118307466Ssephestatic void vmbus_synic_setup(void *); 119307466Ssephestatic void vmbus_synic_teardown(void *); 120307291Ssephestatic int vmbus_sysctl_version(SYSCTL_HANDLER_ARGS); 121307466Ssephestatic int vmbus_dma_alloc(struct vmbus_softc *); 122307466Ssephestatic void vmbus_dma_free(struct vmbus_softc *); 123307466Ssephestatic int vmbus_intr_setup(struct vmbus_softc *); 124307466Ssephestatic void vmbus_intr_teardown(struct vmbus_softc *); 125307466Ssephestatic int vmbus_doattach(struct vmbus_softc *); 126307466Ssephestatic void vmbus_event_proc_dummy(struct vmbus_softc *, 127307466Ssephe int); 128307291Ssephe 129307466Ssephestatic struct vmbus_softc *vmbus_sc; 130300102Ssephe 131301015Ssepheextern inthand_t IDTVEC(vmbus_isr); 132300574Ssephe 133307278Ssephestatic const uint32_t vmbus_version[] = { 134307302Ssephe VMBUS_VERSION_WIN8_1, 135307302Ssephe VMBUS_VERSION_WIN8, 136307302Ssephe VMBUS_VERSION_WIN7, 137307302Ssephe VMBUS_VERSION_WS2008 138307278Ssephe}; 139307278Ssephe 140307451Ssephestatic const vmbus_chanmsg_proc_t 141307451Ssephevmbus_chanmsg_handlers[VMBUS_CHANMSG_TYPE_MAX] = { 142307451Ssephe VMBUS_CHANMSG_PROC(CHOFFER_DONE, vmbus_scan_done), 143307451Ssephe VMBUS_CHANMSG_PROC_WAKEUP(CONNECT_RESP) 144307451Ssephe}; 145307451Ssephe 146307466Ssephestatic device_method_t vmbus_methods[] = { 147307466Ssephe /* Device interface */ 148318392Ssephe DEVMETHOD(device_identify, vmbus_identify), 149307466Ssephe DEVMETHOD(device_probe, vmbus_probe), 150307466Ssephe DEVMETHOD(device_attach, vmbus_attach), 151307466Ssephe DEVMETHOD(device_detach, vmbus_detach), 152307466Ssephe DEVMETHOD(device_shutdown, bus_generic_shutdown), 153307466Ssephe DEVMETHOD(device_suspend, bus_generic_suspend), 154307466Ssephe DEVMETHOD(device_resume, bus_generic_resume), 155307466Ssephe 156307466Ssephe /* Bus interface */ 157307466Ssephe DEVMETHOD(bus_add_child, bus_generic_add_child), 158307466Ssephe DEVMETHOD(bus_print_child, bus_generic_print_child), 159307466Ssephe DEVMETHOD(bus_read_ivar, vmbus_read_ivar), 160307466Ssephe DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str), 161309312Sdexuan DEVMETHOD(bus_alloc_resource, vmbus_alloc_resource), 162309312Sdexuan DEVMETHOD(bus_release_resource, bus_generic_release_resource), 163309312Sdexuan DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 164309312Sdexuan DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 165309312Sdexuan DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 166309312Sdexuan DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 167309312Sdexuan#if __FreeBSD_version >= 1100000 168309312Sdexuan DEVMETHOD(bus_get_cpus, bus_generic_get_cpus), 169309312Sdexuan#endif 170307466Ssephe 171309312Sdexuan /* pcib interface */ 172309312Sdexuan DEVMETHOD(pcib_alloc_msi, vmbus_alloc_msi), 173309312Sdexuan DEVMETHOD(pcib_release_msi, vmbus_release_msi), 174309312Sdexuan DEVMETHOD(pcib_alloc_msix, vmbus_alloc_msix), 175309312Sdexuan DEVMETHOD(pcib_release_msix, vmbus_release_msix), 176309312Sdexuan DEVMETHOD(pcib_map_msi, vmbus_map_msi), 177309312Sdexuan 178307466Ssephe /* Vmbus interface */ 179307466Ssephe DEVMETHOD(vmbus_get_version, vmbus_get_version_method), 180307466Ssephe DEVMETHOD(vmbus_probe_guid, vmbus_probe_guid_method), 181309312Sdexuan DEVMETHOD(vmbus_get_vcpu_id, vmbus_get_vcpu_id_method), 182311368Ssephe DEVMETHOD(vmbus_get_event_taskq, vmbus_get_eventtq_method), 183307466Ssephe 184307466Ssephe DEVMETHOD_END 185307466Ssephe}; 186307466Ssephe 187307466Ssephestatic driver_t vmbus_driver = { 188307466Ssephe "vmbus", 189307466Ssephe vmbus_methods, 190307466Ssephe sizeof(struct vmbus_softc) 191307466Ssephe}; 192307466Ssephe 193307466Ssephestatic devclass_t vmbus_devclass; 194307466Ssephe 195318392SsepheDRIVER_MODULE(vmbus, pcib, vmbus_driver, vmbus_devclass, NULL, NULL); 196318392SsepheDRIVER_MODULE(vmbus, acpi_syscontainer, vmbus_driver, vmbus_devclass, 197318392Ssephe NULL, NULL); 198318392Ssephe 199307466SsepheMODULE_DEPEND(vmbus, acpi, 1, 1, 1); 200309312SdexuanMODULE_DEPEND(vmbus, pci, 1, 1, 1); 201307466SsepheMODULE_VERSION(vmbus, 1); 202307466Ssephe 203307466Ssephestatic __inline struct vmbus_softc * 204307466Ssephevmbus_get_softc(void) 205307466Ssephe{ 206307466Ssephe return vmbus_sc; 207307466Ssephe} 208307466Ssephe 209307301Ssephevoid 210307301Ssephevmbus_msghc_reset(struct vmbus_msghc *mh, size_t dsize) 211307301Ssephe{ 212307301Ssephe struct hypercall_postmsg_in *inprm; 213307301Ssephe 214307301Ssephe if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX) 215307301Ssephe panic("invalid data size %zu", dsize); 216307301Ssephe 217307475Ssephe inprm = vmbus_xact_req_data(mh->mh_xact); 218307301Ssephe memset(inprm, 0, HYPERCALL_POSTMSGIN_SIZE); 219307301Ssephe inprm->hc_connid = VMBUS_CONNID_MESSAGE; 220307301Ssephe inprm->hc_msgtype = HYPERV_MSGTYPE_CHANNEL; 221307301Ssephe inprm->hc_dsize = dsize; 222307301Ssephe} 223307301Ssephe 224307278Ssephestruct vmbus_msghc * 225307278Ssephevmbus_msghc_get(struct vmbus_softc *sc, size_t dsize) 226307278Ssephe{ 227307278Ssephe struct vmbus_msghc *mh; 228307475Ssephe struct vmbus_xact *xact; 229307278Ssephe 230307278Ssephe if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX) 231307301Ssephe panic("invalid data size %zu", dsize); 232307278Ssephe 233307475Ssephe xact = vmbus_xact_get(sc->vmbus_xc, 234307475Ssephe dsize + __offsetof(struct hypercall_postmsg_in, hc_data[0])); 235307475Ssephe if (xact == NULL) 236307475Ssephe return (NULL); 237307278Ssephe 238307475Ssephe mh = vmbus_xact_priv(xact, sizeof(*mh)); 239307475Ssephe mh->mh_xact = xact; 240307475Ssephe 241307301Ssephe vmbus_msghc_reset(mh, dsize); 242307475Ssephe return (mh); 243307278Ssephe} 244307278Ssephe 245307278Ssephevoid 246307475Ssephevmbus_msghc_put(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh) 247307278Ssephe{ 248307278Ssephe 249307475Ssephe vmbus_xact_put(mh->mh_xact); 250307278Ssephe} 251307278Ssephe 252307278Ssephevoid * 253307278Ssephevmbus_msghc_dataptr(struct vmbus_msghc *mh) 254307278Ssephe{ 255307475Ssephe struct hypercall_postmsg_in *inprm; 256307278Ssephe 257307475Ssephe inprm = vmbus_xact_req_data(mh->mh_xact); 258307475Ssephe return (inprm->hc_data); 259307278Ssephe} 260307278Ssephe 261307278Ssepheint 262307278Ssephevmbus_msghc_exec_noresult(struct vmbus_msghc *mh) 263307278Ssephe{ 264307278Ssephe sbintime_t time = SBT_1MS; 265307475Ssephe struct hypercall_postmsg_in *inprm; 266307475Ssephe bus_addr_t inprm_paddr; 267307278Ssephe int i; 268307278Ssephe 269307475Ssephe inprm = vmbus_xact_req_data(mh->mh_xact); 270307475Ssephe inprm_paddr = vmbus_xact_req_paddr(mh->mh_xact); 271307475Ssephe 272307278Ssephe /* 273307278Ssephe * Save the input parameter so that we could restore the input 274307278Ssephe * parameter if the Hypercall failed. 275307278Ssephe * 276307278Ssephe * XXX 277307278Ssephe * Is this really necessary?! i.e. Will the Hypercall ever 278307278Ssephe * overwrite the input parameter? 279307278Ssephe */ 280307475Ssephe memcpy(&mh->mh_inprm_save, inprm, HYPERCALL_POSTMSGIN_SIZE); 281307278Ssephe 282307278Ssephe /* 283307278Ssephe * In order to cope with transient failures, e.g. insufficient 284307278Ssephe * resources on host side, we retry the post message Hypercall 285307278Ssephe * several times. 20 retries seem sufficient. 286307278Ssephe */ 287307278Ssephe#define HC_RETRY_MAX 20 288307278Ssephe 289307278Ssephe for (i = 0; i < HC_RETRY_MAX; ++i) { 290307278Ssephe uint64_t status; 291307278Ssephe 292307475Ssephe status = hypercall_post_message(inprm_paddr); 293307278Ssephe if (status == HYPERCALL_STATUS_SUCCESS) 294307278Ssephe return 0; 295307278Ssephe 296307278Ssephe pause_sbt("hcpmsg", time, 0, C_HARDCLOCK); 297307278Ssephe if (time < SBT_1S * 2) 298307278Ssephe time *= 2; 299307278Ssephe 300307278Ssephe /* Restore input parameter and try again */ 301307475Ssephe memcpy(inprm, &mh->mh_inprm_save, HYPERCALL_POSTMSGIN_SIZE); 302307278Ssephe } 303307278Ssephe 304307278Ssephe#undef HC_RETRY_MAX 305307278Ssephe 306307278Ssephe return EIO; 307307278Ssephe} 308307278Ssephe 309307278Ssepheint 310307475Ssephevmbus_msghc_exec(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh) 311307278Ssephe{ 312307278Ssephe int error; 313307278Ssephe 314307475Ssephe vmbus_xact_activate(mh->mh_xact); 315307278Ssephe error = vmbus_msghc_exec_noresult(mh); 316307475Ssephe if (error) 317307475Ssephe vmbus_xact_deactivate(mh->mh_xact); 318307278Ssephe return error; 319307278Ssephe} 320307278Ssephe 321311367Ssephevoid 322311367Ssephevmbus_msghc_exec_cancel(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh) 323311367Ssephe{ 324311367Ssephe 325311367Ssephe vmbus_xact_deactivate(mh->mh_xact); 326311367Ssephe} 327311367Ssephe 328307278Ssepheconst struct vmbus_message * 329307475Ssephevmbus_msghc_wait_result(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh) 330307278Ssephe{ 331307475Ssephe size_t resp_len; 332307278Ssephe 333307475Ssephe return (vmbus_xact_wait(mh->mh_xact, &resp_len)); 334307278Ssephe} 335307278Ssephe 336311367Ssepheconst struct vmbus_message * 337311367Ssephevmbus_msghc_poll_result(struct vmbus_softc *sc __unused, struct vmbus_msghc *mh) 338311367Ssephe{ 339311367Ssephe size_t resp_len; 340311367Ssephe 341311367Ssephe return (vmbus_xact_poll(mh->mh_xact, &resp_len)); 342311367Ssephe} 343311367Ssephe 344307278Ssephevoid 345307278Ssephevmbus_msghc_wakeup(struct vmbus_softc *sc, const struct vmbus_message *msg) 346307278Ssephe{ 347307278Ssephe 348307475Ssephe vmbus_xact_ctx_wakeup(sc->vmbus_xc, msg, sizeof(*msg)); 349307278Ssephe} 350307278Ssephe 351307302Ssepheuint32_t 352307302Ssephevmbus_gpadl_alloc(struct vmbus_softc *sc) 353307302Ssephe{ 354311366Ssephe uint32_t gpadl; 355311366Ssephe 356311366Ssepheagain: 357311366Ssephe gpadl = atomic_fetchadd_int(&sc->vmbus_gpadl, 1); 358311366Ssephe if (gpadl == 0) 359311366Ssephe goto again; 360311366Ssephe return (gpadl); 361307302Ssephe} 362307302Ssephe 363307278Ssephestatic int 364307302Ssephevmbus_connect(struct vmbus_softc *sc, uint32_t version) 365307278Ssephe{ 366307302Ssephe struct vmbus_chanmsg_connect *req; 367307278Ssephe const struct vmbus_message *msg; 368307278Ssephe struct vmbus_msghc *mh; 369307302Ssephe int error, done = 0; 370307278Ssephe 371307278Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 372307278Ssephe if (mh == NULL) 373307278Ssephe return ENXIO; 374307278Ssephe 375307278Ssephe req = vmbus_msghc_dataptr(mh); 376307302Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CONNECT; 377307278Ssephe req->chm_ver = version; 378307278Ssephe req->chm_evtflags = sc->vmbus_evtflags_dma.hv_paddr; 379307278Ssephe req->chm_mnf1 = sc->vmbus_mnf1_dma.hv_paddr; 380307278Ssephe req->chm_mnf2 = sc->vmbus_mnf2_dma.hv_paddr; 381307278Ssephe 382307278Ssephe error = vmbus_msghc_exec(sc, mh); 383307278Ssephe if (error) { 384307278Ssephe vmbus_msghc_put(sc, mh); 385307278Ssephe return error; 386307278Ssephe } 387307278Ssephe 388307278Ssephe msg = vmbus_msghc_wait_result(sc, mh); 389307302Ssephe done = ((const struct vmbus_chanmsg_connect_resp *) 390307302Ssephe msg->msg_data)->chm_done; 391307278Ssephe 392307278Ssephe vmbus_msghc_put(sc, mh); 393307278Ssephe 394307302Ssephe return (done ? 0 : EOPNOTSUPP); 395307278Ssephe} 396307278Ssephe 397307278Ssephestatic int 398307278Ssephevmbus_init(struct vmbus_softc *sc) 399307278Ssephe{ 400307278Ssephe int i; 401307278Ssephe 402307278Ssephe for (i = 0; i < nitems(vmbus_version); ++i) { 403307278Ssephe int error; 404307278Ssephe 405307302Ssephe error = vmbus_connect(sc, vmbus_version[i]); 406307278Ssephe if (!error) { 407307302Ssephe sc->vmbus_version = vmbus_version[i]; 408307278Ssephe device_printf(sc->vmbus_dev, "version %u.%u\n", 409307302Ssephe VMBUS_VERSION_MAJOR(sc->vmbus_version), 410307302Ssephe VMBUS_VERSION_MINOR(sc->vmbus_version)); 411307278Ssephe return 0; 412307278Ssephe } 413307278Ssephe } 414307278Ssephe return ENXIO; 415307278Ssephe} 416307278Ssephe 417307278Ssephestatic void 418307302Ssephevmbus_disconnect(struct vmbus_softc *sc) 419307291Ssephe{ 420307302Ssephe struct vmbus_chanmsg_disconnect *req; 421307291Ssephe struct vmbus_msghc *mh; 422307291Ssephe int error; 423307291Ssephe 424307291Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 425307291Ssephe if (mh == NULL) { 426307291Ssephe device_printf(sc->vmbus_dev, 427307302Ssephe "can not get msg hypercall for disconnect\n"); 428307291Ssephe return; 429307291Ssephe } 430307291Ssephe 431307291Ssephe req = vmbus_msghc_dataptr(mh); 432307302Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_DISCONNECT; 433307291Ssephe 434307291Ssephe error = vmbus_msghc_exec_noresult(mh); 435307291Ssephe vmbus_msghc_put(sc, mh); 436307291Ssephe 437307291Ssephe if (error) { 438307291Ssephe device_printf(sc->vmbus_dev, 439307302Ssephe "disconnect msg hypercall failed\n"); 440307291Ssephe } 441307291Ssephe} 442307291Ssephe 443307291Ssephestatic int 444307291Ssephevmbus_req_channels(struct vmbus_softc *sc) 445307291Ssephe{ 446307302Ssephe struct vmbus_chanmsg_chrequest *req; 447307291Ssephe struct vmbus_msghc *mh; 448307291Ssephe int error; 449307291Ssephe 450307291Ssephe mh = vmbus_msghc_get(sc, sizeof(*req)); 451307291Ssephe if (mh == NULL) 452307291Ssephe return ENXIO; 453307291Ssephe 454307291Ssephe req = vmbus_msghc_dataptr(mh); 455307302Ssephe req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_CHREQUEST; 456307291Ssephe 457307291Ssephe error = vmbus_msghc_exec_noresult(mh); 458307291Ssephe vmbus_msghc_put(sc, mh); 459307291Ssephe 460307291Ssephe return error; 461307291Ssephe} 462307291Ssephe 463307451Ssephestatic void 464307599Ssephevmbus_scan_done_task(void *xsc, int pending __unused) 465307291Ssephe{ 466307599Ssephe struct vmbus_softc *sc = xsc; 467307599Ssephe 468307599Ssephe mtx_lock(&Giant); 469307599Ssephe sc->vmbus_scandone = true; 470307599Ssephe mtx_unlock(&Giant); 471307599Ssephe wakeup(&sc->vmbus_scandone); 472307291Ssephe} 473307291Ssephe 474307451Ssephestatic void 475307451Ssephevmbus_scan_done(struct vmbus_softc *sc, 476307451Ssephe const struct vmbus_message *msg __unused) 477307291Ssephe{ 478307291Ssephe 479307599Ssephe taskqueue_enqueue(sc->vmbus_devtq, &sc->vmbus_scandone_task); 480307291Ssephe} 481307291Ssephe 482307291Ssephestatic int 483307291Ssephevmbus_scan(struct vmbus_softc *sc) 484307291Ssephe{ 485307291Ssephe int error; 486307291Ssephe 487307291Ssephe /* 488307599Ssephe * Identify, probe and attach for non-channel devices. 489307599Ssephe */ 490307599Ssephe bus_generic_probe(sc->vmbus_dev); 491307599Ssephe bus_generic_attach(sc->vmbus_dev); 492307599Ssephe 493307599Ssephe /* 494307599Ssephe * This taskqueue serializes vmbus devices' attach and detach 495307599Ssephe * for channel offer and rescind messages. 496307599Ssephe */ 497307599Ssephe sc->vmbus_devtq = taskqueue_create("vmbus dev", M_WAITOK, 498307599Ssephe taskqueue_thread_enqueue, &sc->vmbus_devtq); 499307599Ssephe taskqueue_start_threads(&sc->vmbus_devtq, 1, PI_NET, "vmbusdev"); 500307599Ssephe TASK_INIT(&sc->vmbus_scandone_task, 0, vmbus_scan_done_task, sc); 501307599Ssephe 502307599Ssephe /* 503307599Ssephe * This taskqueue handles sub-channel detach, so that vmbus 504307599Ssephe * device's detach running in vmbus_devtq can drain its sub- 505307599Ssephe * channels. 506307599Ssephe */ 507307599Ssephe sc->vmbus_subchtq = taskqueue_create("vmbus subch", M_WAITOK, 508307599Ssephe taskqueue_thread_enqueue, &sc->vmbus_subchtq); 509307599Ssephe taskqueue_start_threads(&sc->vmbus_subchtq, 1, PI_NET, "vmbussch"); 510307599Ssephe 511307599Ssephe /* 512307291Ssephe * Start vmbus scanning. 513307291Ssephe */ 514307291Ssephe error = vmbus_req_channels(sc); 515307291Ssephe if (error) { 516307291Ssephe device_printf(sc->vmbus_dev, "channel request failed: %d\n", 517307291Ssephe error); 518307599Ssephe return (error); 519307291Ssephe } 520307291Ssephe 521307291Ssephe /* 522307599Ssephe * Wait for all vmbus devices from the initial channel offers to be 523307599Ssephe * attached. 524307291Ssephe */ 525307599Ssephe GIANT_REQUIRED; 526307599Ssephe while (!sc->vmbus_scandone) 527307599Ssephe mtx_sleep(&sc->vmbus_scandone, &Giant, 0, "vmbusdev", 0); 528307291Ssephe 529307291Ssephe if (bootverbose) { 530307291Ssephe device_printf(sc->vmbus_dev, "device scan, probe and attach " 531307291Ssephe "done\n"); 532307291Ssephe } 533307599Ssephe return (0); 534307291Ssephe} 535307291Ssephe 536307291Ssephestatic void 537307599Ssephevmbus_scan_teardown(struct vmbus_softc *sc) 538307599Ssephe{ 539307599Ssephe 540307599Ssephe GIANT_REQUIRED; 541307599Ssephe if (sc->vmbus_devtq != NULL) { 542307599Ssephe mtx_unlock(&Giant); 543307599Ssephe taskqueue_free(sc->vmbus_devtq); 544307599Ssephe mtx_lock(&Giant); 545307599Ssephe sc->vmbus_devtq = NULL; 546307599Ssephe } 547307599Ssephe if (sc->vmbus_subchtq != NULL) { 548307599Ssephe mtx_unlock(&Giant); 549307599Ssephe taskqueue_free(sc->vmbus_subchtq); 550307599Ssephe mtx_lock(&Giant); 551307599Ssephe sc->vmbus_subchtq = NULL; 552307599Ssephe } 553307599Ssephe} 554307599Ssephe 555307599Ssephestatic void 556307451Ssephevmbus_chanmsg_handle(struct vmbus_softc *sc, const struct vmbus_message *msg) 557307451Ssephe{ 558307451Ssephe vmbus_chanmsg_proc_t msg_proc; 559307451Ssephe uint32_t msg_type; 560307451Ssephe 561307451Ssephe msg_type = ((const struct vmbus_chanmsg_hdr *)msg->msg_data)->chm_type; 562307451Ssephe if (msg_type >= VMBUS_CHANMSG_TYPE_MAX) { 563307451Ssephe device_printf(sc->vmbus_dev, "unknown message type 0x%x\n", 564307451Ssephe msg_type); 565307451Ssephe return; 566307451Ssephe } 567307451Ssephe 568307451Ssephe msg_proc = vmbus_chanmsg_handlers[msg_type]; 569307451Ssephe if (msg_proc != NULL) 570307451Ssephe msg_proc(sc, msg); 571307451Ssephe 572307451Ssephe /* Channel specific processing */ 573307451Ssephe vmbus_chan_msgproc(sc, msg); 574307451Ssephe} 575307451Ssephe 576307451Ssephestatic void 577300572Ssephevmbus_msg_task(void *xsc, int pending __unused) 578250199Sgrehan{ 579300572Ssephe struct vmbus_softc *sc = xsc; 580301019Ssephe volatile struct vmbus_message *msg; 581250199Sgrehan 582300988Ssephe msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE; 583300108Ssephe for (;;) { 584307278Ssephe if (msg->msg_type == HYPERV_MSGTYPE_NONE) { 585301484Ssephe /* No message */ 586301484Ssephe break; 587307278Ssephe } else if (msg->msg_type == HYPERV_MSGTYPE_CHANNEL) { 588301487Ssephe /* Channel message */ 589307451Ssephe vmbus_chanmsg_handle(sc, 590301488Ssephe __DEVOLATILE(const struct vmbus_message *, msg)); 591301484Ssephe } 592292861Sdelphij 593307278Ssephe msg->msg_type = HYPERV_MSGTYPE_NONE; 594300108Ssephe /* 595301019Ssephe * Make sure the write to msg_type (i.e. set to 596307278Ssephe * HYPERV_MSGTYPE_NONE) happens before we read the 597301019Ssephe * msg_flags and EOMing. Otherwise, the EOMing will 598301019Ssephe * not deliver any more messages since there is no 599301019Ssephe * empty slot 600300108Ssephe * 601300108Ssephe * NOTE: 602300108Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 603300108Ssephe * will become compiler fence on UP kernel. 604300108Ssephe */ 605300108Ssephe mb(); 606301019Ssephe if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) { 607250199Sgrehan /* 608250199Sgrehan * This will cause message queue rescan to possibly 609250199Sgrehan * deliver another msg from the hypervisor 610250199Sgrehan */ 611300830Ssephe wrmsr(MSR_HV_EOM, 0); 612300108Ssephe } 613250199Sgrehan } 614250199Sgrehan} 615250199Sgrehan 616301015Ssephestatic __inline int 617301015Ssephevmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu) 618250199Sgrehan{ 619301019Ssephe volatile struct vmbus_message *msg; 620301019Ssephe struct vmbus_message *msg_base; 621250199Sgrehan 622301009Ssephe msg_base = VMBUS_PCPU_GET(sc, message, cpu); 623301009Ssephe 624250199Sgrehan /* 625301009Ssephe * Check event timer. 626301009Ssephe * 627301009Ssephe * TODO: move this to independent IDT vector. 628250199Sgrehan */ 629300988Ssephe msg = msg_base + VMBUS_SINT_TIMER; 630307278Ssephe if (msg->msg_type == HYPERV_MSGTYPE_TIMER_EXPIRED) { 631307278Ssephe msg->msg_type = HYPERV_MSGTYPE_NONE; 632293873Ssephe 633300988Ssephe vmbus_et_intr(frame); 634297176Ssephe 635293873Ssephe /* 636301019Ssephe * Make sure the write to msg_type (i.e. set to 637307278Ssephe * HYPERV_MSGTYPE_NONE) happens before we read the 638301019Ssephe * msg_flags and EOMing. Otherwise, the EOMing will 639301019Ssephe * not deliver any more messages since there is no 640301019Ssephe * empty slot 641297634Ssephe * 642297634Ssephe * NOTE: 643297634Ssephe * mb() is used here, since atomic_thread_fence_seq_cst() 644297636Ssephe * will become compiler fence on UP kernel. 645293873Ssephe */ 646297634Ssephe mb(); 647301019Ssephe if (msg->msg_flags & VMBUS_MSGFLAG_PENDING) { 648293873Ssephe /* 649293873Ssephe * This will cause message queue rescan to possibly 650293873Ssephe * deliver another msg from the hypervisor 651293873Ssephe */ 652300830Ssephe wrmsr(MSR_HV_EOM, 0); 653293873Ssephe } 654293873Ssephe } 655293873Ssephe 656301009Ssephe /* 657301009Ssephe * Check events. Hot path for network and storage I/O data; high rate. 658301009Ssephe * 659301009Ssephe * NOTE: 660301009Ssephe * As recommended by the Windows guest fellows, we check events before 661301009Ssephe * checking messages. 662301009Ssephe */ 663301009Ssephe sc->vmbus_event_proc(sc, cpu); 664301009Ssephe 665301009Ssephe /* 666301009Ssephe * Check messages. Mainly management stuffs; ultra low rate. 667301009Ssephe */ 668300988Ssephe msg = msg_base + VMBUS_SINT_MESSAGE; 669307278Ssephe if (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) { 670300646Ssephe taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu), 671300646Ssephe VMBUS_PCPU_PTR(sc, message_task, cpu)); 672250199Sgrehan } 673250199Sgrehan 674293873Ssephe return (FILTER_HANDLED); 675250199Sgrehan} 676250199Sgrehan 677282212Swhuvoid 678301015Ssephevmbus_handle_intr(struct trapframe *trap_frame) 679282212Swhu{ 680300565Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 681300565Ssephe int cpu = curcpu; 682282212Swhu 683282212Swhu /* 684282212Swhu * Disable preemption. 685282212Swhu */ 686282212Swhu critical_enter(); 687282212Swhu 688282212Swhu /* 689282212Swhu * Do a little interrupt counting. 690282212Swhu */ 691300573Ssephe (*VMBUS_PCPU_GET(sc, intr_cnt, cpu))++; 692282212Swhu 693301015Ssephe vmbus_handle_intr1(sc, trap_frame, cpu); 694282212Swhu 695282212Swhu /* 696282212Swhu * Enable preemption. 697282212Swhu */ 698282212Swhu critical_exit(); 699282212Swhu} 700282212Swhu 701300571Ssephestatic void 702300652Ssephevmbus_synic_setup(void *xsc) 703300571Ssephe{ 704300652Ssephe struct vmbus_softc *sc = xsc; 705300654Ssephe int cpu = curcpu; 706300654Ssephe uint64_t val, orig; 707300654Ssephe uint32_t sint; 708300571Ssephe 709300834Ssephe if (hyperv_features & CPUID_HV_MSR_VP_INDEX) { 710307454Ssephe /* Save virtual processor id. */ 711300834Ssephe VMBUS_PCPU_GET(sc, vcpuid, cpu) = rdmsr(MSR_HV_VP_INDEX); 712300834Ssephe } else { 713307454Ssephe /* Set virtual processor id to 0 for compatibility. */ 714307454Ssephe VMBUS_PCPU_GET(sc, vcpuid, cpu) = 0; 715300834Ssephe } 716300571Ssephe 717300571Ssephe /* 718300654Ssephe * Setup the SynIC message. 719300571Ssephe */ 720300654Ssephe orig = rdmsr(MSR_HV_SIMP); 721300654Ssephe val = MSR_HV_SIMP_ENABLE | (orig & MSR_HV_SIMP_RSVD_MASK) | 722300654Ssephe ((VMBUS_PCPU_GET(sc, message_dma.hv_paddr, cpu) >> PAGE_SHIFT) << 723300654Ssephe MSR_HV_SIMP_PGSHIFT); 724300654Ssephe wrmsr(MSR_HV_SIMP, val); 725300571Ssephe 726300571Ssephe /* 727300654Ssephe * Setup the SynIC event flags. 728300571Ssephe */ 729300654Ssephe orig = rdmsr(MSR_HV_SIEFP); 730300654Ssephe val = MSR_HV_SIEFP_ENABLE | (orig & MSR_HV_SIEFP_RSVD_MASK) | 731301106Ssephe ((VMBUS_PCPU_GET(sc, event_flags_dma.hv_paddr, cpu) 732301106Ssephe >> PAGE_SHIFT) << MSR_HV_SIEFP_PGSHIFT); 733300654Ssephe wrmsr(MSR_HV_SIEFP, val); 734300571Ssephe 735300571Ssephe 736300654Ssephe /* 737300654Ssephe * Configure and unmask SINT for message and event flags. 738300654Ssephe */ 739300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE; 740300654Ssephe orig = rdmsr(sint); 741300654Ssephe val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI | 742300654Ssephe (orig & MSR_HV_SINT_RSVD_MASK); 743300654Ssephe wrmsr(sint, val); 744300571Ssephe 745300654Ssephe /* 746300654Ssephe * Configure and unmask SINT for timer. 747300654Ssephe */ 748300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER; 749300654Ssephe orig = rdmsr(sint); 750300654Ssephe val = sc->vmbus_idtvec | MSR_HV_SINT_AUTOEOI | 751300654Ssephe (orig & MSR_HV_SINT_RSVD_MASK); 752300654Ssephe wrmsr(sint, val); 753300571Ssephe 754300571Ssephe /* 755300654Ssephe * All done; enable SynIC. 756300571Ssephe */ 757300654Ssephe orig = rdmsr(MSR_HV_SCONTROL); 758300654Ssephe val = MSR_HV_SCTRL_ENABLE | (orig & MSR_HV_SCTRL_RSVD_MASK); 759300654Ssephe wrmsr(MSR_HV_SCONTROL, val); 760300571Ssephe} 761300571Ssephe 762300571Ssephestatic void 763300571Ssephevmbus_synic_teardown(void *arg) 764300571Ssephe{ 765300654Ssephe uint64_t orig; 766300654Ssephe uint32_t sint; 767300571Ssephe 768300654Ssephe /* 769300654Ssephe * Disable SynIC. 770300654Ssephe */ 771300654Ssephe orig = rdmsr(MSR_HV_SCONTROL); 772300654Ssephe wrmsr(MSR_HV_SCONTROL, (orig & MSR_HV_SCTRL_RSVD_MASK)); 773300571Ssephe 774300654Ssephe /* 775300654Ssephe * Mask message and event flags SINT. 776300654Ssephe */ 777300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_MESSAGE; 778300654Ssephe orig = rdmsr(sint); 779300654Ssephe wrmsr(sint, orig | MSR_HV_SINT_MASKED); 780300571Ssephe 781300571Ssephe /* 782300654Ssephe * Mask timer SINT. 783300571Ssephe */ 784300988Ssephe sint = MSR_HV_SINT0 + VMBUS_SINT_TIMER; 785300654Ssephe orig = rdmsr(sint); 786300654Ssephe wrmsr(sint, orig | MSR_HV_SINT_MASKED); 787300571Ssephe 788300654Ssephe /* 789300654Ssephe * Teardown SynIC message. 790300654Ssephe */ 791300654Ssephe orig = rdmsr(MSR_HV_SIMP); 792300654Ssephe wrmsr(MSR_HV_SIMP, (orig & MSR_HV_SIMP_RSVD_MASK)); 793300571Ssephe 794300571Ssephe /* 795300654Ssephe * Teardown SynIC event flags. 796300571Ssephe */ 797300654Ssephe orig = rdmsr(MSR_HV_SIEFP); 798300654Ssephe wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK)); 799300571Ssephe} 800300571Ssephe 801300644Ssephestatic int 802300572Ssephevmbus_dma_alloc(struct vmbus_softc *sc) 803300572Ssephe{ 804301583Ssephe bus_dma_tag_t parent_dtag; 805301583Ssephe uint8_t *evtflags; 806300572Ssephe int cpu; 807300572Ssephe 808301583Ssephe parent_dtag = bus_get_dma_tag(sc->vmbus_dev); 809300572Ssephe CPU_FOREACH(cpu) { 810300644Ssephe void *ptr; 811300644Ssephe 812300572Ssephe /* 813300572Ssephe * Per-cpu messages and event flags. 814300572Ssephe */ 815301583Ssephe ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, 816301583Ssephe PAGE_SIZE, VMBUS_PCPU_PTR(sc, message_dma, cpu), 817300572Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 818300644Ssephe if (ptr == NULL) 819300644Ssephe return ENOMEM; 820300644Ssephe VMBUS_PCPU_GET(sc, message, cpu) = ptr; 821300644Ssephe 822301583Ssephe ptr = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, 823301583Ssephe PAGE_SIZE, VMBUS_PCPU_PTR(sc, event_flags_dma, cpu), 824300572Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 825300644Ssephe if (ptr == NULL) 826300644Ssephe return ENOMEM; 827301106Ssephe VMBUS_PCPU_GET(sc, event_flags, cpu) = ptr; 828300572Ssephe } 829301583Ssephe 830301583Ssephe evtflags = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, 831301583Ssephe PAGE_SIZE, &sc->vmbus_evtflags_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); 832301583Ssephe if (evtflags == NULL) 833301583Ssephe return ENOMEM; 834301583Ssephe sc->vmbus_rx_evtflags = (u_long *)evtflags; 835301588Ssephe sc->vmbus_tx_evtflags = (u_long *)(evtflags + (PAGE_SIZE / 2)); 836301583Ssephe sc->vmbus_evtflags = evtflags; 837301583Ssephe 838301583Ssephe sc->vmbus_mnf1 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, 839301583Ssephe PAGE_SIZE, &sc->vmbus_mnf1_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); 840301583Ssephe if (sc->vmbus_mnf1 == NULL) 841301583Ssephe return ENOMEM; 842301583Ssephe 843301583Ssephe sc->vmbus_mnf2 = hyperv_dmamem_alloc(parent_dtag, PAGE_SIZE, 0, 844307310Ssephe sizeof(struct vmbus_mnf), &sc->vmbus_mnf2_dma, 845307310Ssephe BUS_DMA_WAITOK | BUS_DMA_ZERO); 846301583Ssephe if (sc->vmbus_mnf2 == NULL) 847301583Ssephe return ENOMEM; 848301583Ssephe 849300644Ssephe return 0; 850300572Ssephe} 851300572Ssephe 852300572Ssephestatic void 853300572Ssephevmbus_dma_free(struct vmbus_softc *sc) 854300572Ssephe{ 855300572Ssephe int cpu; 856300572Ssephe 857301583Ssephe if (sc->vmbus_evtflags != NULL) { 858301583Ssephe hyperv_dmamem_free(&sc->vmbus_evtflags_dma, sc->vmbus_evtflags); 859301583Ssephe sc->vmbus_evtflags = NULL; 860301583Ssephe sc->vmbus_rx_evtflags = NULL; 861301583Ssephe sc->vmbus_tx_evtflags = NULL; 862301583Ssephe } 863301583Ssephe if (sc->vmbus_mnf1 != NULL) { 864301583Ssephe hyperv_dmamem_free(&sc->vmbus_mnf1_dma, sc->vmbus_mnf1); 865301583Ssephe sc->vmbus_mnf1 = NULL; 866301583Ssephe } 867301583Ssephe if (sc->vmbus_mnf2 != NULL) { 868301583Ssephe hyperv_dmamem_free(&sc->vmbus_mnf2_dma, sc->vmbus_mnf2); 869301583Ssephe sc->vmbus_mnf2 = NULL; 870301583Ssephe } 871301583Ssephe 872300572Ssephe CPU_FOREACH(cpu) { 873300573Ssephe if (VMBUS_PCPU_GET(sc, message, cpu) != NULL) { 874300572Ssephe hyperv_dmamem_free( 875300573Ssephe VMBUS_PCPU_PTR(sc, message_dma, cpu), 876300573Ssephe VMBUS_PCPU_GET(sc, message, cpu)); 877300573Ssephe VMBUS_PCPU_GET(sc, message, cpu) = NULL; 878300572Ssephe } 879301106Ssephe if (VMBUS_PCPU_GET(sc, event_flags, cpu) != NULL) { 880300572Ssephe hyperv_dmamem_free( 881301106Ssephe VMBUS_PCPU_PTR(sc, event_flags_dma, cpu), 882301106Ssephe VMBUS_PCPU_GET(sc, event_flags, cpu)); 883301106Ssephe VMBUS_PCPU_GET(sc, event_flags, cpu) = NULL; 884300572Ssephe } 885300572Ssephe } 886300572Ssephe} 887300572Ssephe 888250199Sgrehanstatic int 889300574Ssephevmbus_intr_setup(struct vmbus_softc *sc) 890300574Ssephe{ 891300574Ssephe int cpu; 892300574Ssephe 893300574Ssephe CPU_FOREACH(cpu) { 894300574Ssephe char buf[MAXCOMLEN + 1]; 895300645Ssephe cpuset_t cpu_mask; 896300574Ssephe 897300645Ssephe /* Allocate an interrupt counter for Hyper-V interrupt */ 898300574Ssephe snprintf(buf, sizeof(buf), "cpu%d:hyperv", cpu); 899300574Ssephe intrcnt_add(buf, VMBUS_PCPU_PTR(sc, intr_cnt, cpu)); 900300574Ssephe 901300574Ssephe /* 902300645Ssephe * Setup taskqueue to handle events. Task will be per- 903300645Ssephe * channel. 904300574Ssephe */ 905300646Ssephe VMBUS_PCPU_GET(sc, event_tq, cpu) = taskqueue_create_fast( 906300646Ssephe "hyperv event", M_WAITOK, taskqueue_thread_enqueue, 907300646Ssephe VMBUS_PCPU_PTR(sc, event_tq, cpu)); 908300574Ssephe CPU_SETOF(cpu, &cpu_mask); 909300574Ssephe taskqueue_start_threads_cpuset( 910300646Ssephe VMBUS_PCPU_PTR(sc, event_tq, cpu), 1, PI_NET, &cpu_mask, 911300646Ssephe "hvevent%d", cpu); 912300574Ssephe 913300574Ssephe /* 914300645Ssephe * Setup tasks and taskqueues to handle messages. 915300574Ssephe */ 916300646Ssephe VMBUS_PCPU_GET(sc, message_tq, cpu) = taskqueue_create_fast( 917300574Ssephe "hyperv msg", M_WAITOK, taskqueue_thread_enqueue, 918300646Ssephe VMBUS_PCPU_PTR(sc, message_tq, cpu)); 919300574Ssephe CPU_SETOF(cpu, &cpu_mask); 920300574Ssephe taskqueue_start_threads_cpuset( 921300646Ssephe VMBUS_PCPU_PTR(sc, message_tq, cpu), 1, PI_NET, &cpu_mask, 922300646Ssephe "hvmsg%d", cpu); 923300646Ssephe TASK_INIT(VMBUS_PCPU_PTR(sc, message_task, cpu), 0, 924300574Ssephe vmbus_msg_task, sc); 925300574Ssephe } 926300645Ssephe 927300645Ssephe /* 928300645Ssephe * All Hyper-V ISR required resources are setup, now let's find a 929300645Ssephe * free IDT vector for Hyper-V ISR and set it up. 930300645Ssephe */ 931301015Ssephe sc->vmbus_idtvec = lapic_ipi_alloc(IDTVEC(vmbus_isr)); 932300645Ssephe if (sc->vmbus_idtvec < 0) { 933300645Ssephe device_printf(sc->vmbus_dev, "cannot find free IDT vector\n"); 934300645Ssephe return ENXIO; 935300645Ssephe } 936308621Ssephe if (bootverbose) { 937300645Ssephe device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n", 938300645Ssephe sc->vmbus_idtvec); 939300645Ssephe } 940300574Ssephe return 0; 941300574Ssephe} 942300574Ssephe 943300574Ssephestatic void 944300574Ssephevmbus_intr_teardown(struct vmbus_softc *sc) 945300574Ssephe{ 946300574Ssephe int cpu; 947300574Ssephe 948300645Ssephe if (sc->vmbus_idtvec >= 0) { 949300645Ssephe lapic_ipi_free(sc->vmbus_idtvec); 950300645Ssephe sc->vmbus_idtvec = -1; 951300645Ssephe } 952300645Ssephe 953300574Ssephe CPU_FOREACH(cpu) { 954300646Ssephe if (VMBUS_PCPU_GET(sc, event_tq, cpu) != NULL) { 955300646Ssephe taskqueue_free(VMBUS_PCPU_GET(sc, event_tq, cpu)); 956300646Ssephe VMBUS_PCPU_GET(sc, event_tq, cpu) = NULL; 957300574Ssephe } 958300646Ssephe if (VMBUS_PCPU_GET(sc, message_tq, cpu) != NULL) { 959300646Ssephe taskqueue_drain(VMBUS_PCPU_GET(sc, message_tq, cpu), 960300646Ssephe VMBUS_PCPU_PTR(sc, message_task, cpu)); 961300646Ssephe taskqueue_free(VMBUS_PCPU_GET(sc, message_tq, cpu)); 962300646Ssephe VMBUS_PCPU_GET(sc, message_tq, cpu) = NULL; 963300576Ssephe } 964300574Ssephe } 965300574Ssephe} 966300574Ssephe 967300574Ssephestatic int 968300651Ssephevmbus_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 969250199Sgrehan{ 970250199Sgrehan return (ENOENT); 971250199Sgrehan} 972250199Sgrehan 973250199Sgrehanstatic int 974297143Ssephevmbus_child_pnpinfo_str(device_t dev, device_t child, char *buf, size_t buflen) 975297143Ssephe{ 976307461Ssephe const struct vmbus_channel *chan; 977301021Ssephe char guidbuf[HYPERV_GUID_STRLEN]; 978297143Ssephe 979307307Ssephe chan = vmbus_get_channel(child); 980307307Ssephe if (chan == NULL) { 981307307Ssephe /* Event timer device, which does not belong to a channel */ 982298449Ssephe return (0); 983307307Ssephe } 984298449Ssephe 985297143Ssephe strlcat(buf, "classid=", buflen); 986307307Ssephe hyperv_guid2str(&chan->ch_guid_type, guidbuf, sizeof(guidbuf)); 987297143Ssephe strlcat(buf, guidbuf, buflen); 988297143Ssephe 989297143Ssephe strlcat(buf, " deviceid=", buflen); 990307307Ssephe hyperv_guid2str(&chan->ch_guid_inst, guidbuf, sizeof(guidbuf)); 991297143Ssephe strlcat(buf, guidbuf, buflen); 992297143Ssephe 993297143Ssephe return (0); 994297143Ssephe} 995297143Ssephe 996307307Ssepheint 997307461Ssephevmbus_add_child(struct vmbus_channel *chan) 998250199Sgrehan{ 999307461Ssephe struct vmbus_softc *sc = chan->ch_vmbus; 1000307307Ssephe device_t parent = sc->vmbus_dev; 1001250199Sgrehan 1002307599Ssephe mtx_lock(&Giant); 1003307449Ssephe 1004307307Ssephe chan->ch_dev = device_add_child(parent, NULL, -1); 1005307307Ssephe if (chan->ch_dev == NULL) { 1006307599Ssephe mtx_unlock(&Giant); 1007307307Ssephe device_printf(parent, "device_add_child for chan%u failed\n", 1008307307Ssephe chan->ch_id); 1009307599Ssephe return (ENXIO); 1010307307Ssephe } 1011307307Ssephe device_set_ivars(chan->ch_dev, chan); 1012307599Ssephe device_probe_and_attach(chan->ch_dev); 1013250199Sgrehan 1014307599Ssephe mtx_unlock(&Giant); 1015307599Ssephe return (0); 1016250199Sgrehan} 1017250199Sgrehan 1018307307Ssepheint 1019307461Ssephevmbus_delete_child(struct vmbus_channel *chan) 1020250199Sgrehan{ 1021307599Ssephe int error = 0; 1022250199Sgrehan 1023307599Ssephe mtx_lock(&Giant); 1024307599Ssephe if (chan->ch_dev != NULL) { 1025307599Ssephe error = device_delete_child(chan->ch_vmbus->vmbus_dev, 1026307599Ssephe chan->ch_dev); 1027308621Ssephe chan->ch_dev = NULL; 1028297142Ssephe } 1029250199Sgrehan mtx_unlock(&Giant); 1030307599Ssephe return (error); 1031250199Sgrehan} 1032250199Sgrehan 1033250199Sgrehanstatic int 1034307291Ssephevmbus_sysctl_version(SYSCTL_HANDLER_ARGS) 1035307291Ssephe{ 1036307302Ssephe struct vmbus_softc *sc = arg1; 1037307291Ssephe char verstr[16]; 1038307291Ssephe 1039307291Ssephe snprintf(verstr, sizeof(verstr), "%u.%u", 1040307302Ssephe VMBUS_VERSION_MAJOR(sc->vmbus_version), 1041307302Ssephe VMBUS_VERSION_MINOR(sc->vmbus_version)); 1042307291Ssephe return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); 1043307291Ssephe} 1044307291Ssephe 1045309312Sdexuan/* 1046309312Sdexuan * We need the function to make sure the MMIO resource is allocated from the 1047309312Sdexuan * ranges found in _CRS. 1048309312Sdexuan * 1049309312Sdexuan * For the release function, we can use bus_generic_release_resource(). 1050309312Sdexuan */ 1051309312Sdexuanstatic struct resource * 1052309312Sdexuanvmbus_alloc_resource(device_t dev, device_t child, int type, int *rid, 1053309312Sdexuan rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 1054309312Sdexuan{ 1055309312Sdexuan device_t parent = device_get_parent(dev); 1056309312Sdexuan struct resource *res; 1057309312Sdexuan 1058309312Sdexuan#ifdef NEW_PCIB 1059309312Sdexuan if (type == SYS_RES_MEMORY) { 1060309312Sdexuan struct vmbus_softc *sc = device_get_softc(dev); 1061309312Sdexuan 1062309312Sdexuan res = pcib_host_res_alloc(&sc->vmbus_mmio_res, child, type, 1063309312Sdexuan rid, start, end, count, flags); 1064309312Sdexuan } else 1065309312Sdexuan#endif 1066309312Sdexuan { 1067309312Sdexuan res = BUS_ALLOC_RESOURCE(parent, child, type, rid, start, 1068309312Sdexuan end, count, flags); 1069309312Sdexuan } 1070309312Sdexuan 1071309312Sdexuan return (res); 1072309312Sdexuan} 1073309312Sdexuan 1074309312Sdexuanstatic int 1075309312Sdexuanvmbus_alloc_msi(device_t bus, device_t dev, int count, int maxcount, int *irqs) 1076309312Sdexuan{ 1077318392Ssephe 1078318392Ssephe return (PCIB_ALLOC_MSI(device_get_parent(bus), dev, count, maxcount, 1079318392Ssephe irqs)); 1080309312Sdexuan} 1081309312Sdexuan 1082309312Sdexuanstatic int 1083309312Sdexuanvmbus_release_msi(device_t bus, device_t dev, int count, int *irqs) 1084309312Sdexuan{ 1085318392Ssephe 1086318392Ssephe return (PCIB_RELEASE_MSI(device_get_parent(bus), dev, count, irqs)); 1087309312Sdexuan} 1088309312Sdexuan 1089309312Sdexuanstatic int 1090309312Sdexuanvmbus_alloc_msix(device_t bus, device_t dev, int *irq) 1091309312Sdexuan{ 1092318392Ssephe 1093318392Ssephe return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq)); 1094309312Sdexuan} 1095309312Sdexuan 1096309312Sdexuanstatic int 1097309312Sdexuanvmbus_release_msix(device_t bus, device_t dev, int irq) 1098309312Sdexuan{ 1099318392Ssephe 1100318392Ssephe return (PCIB_RELEASE_MSIX(device_get_parent(bus), dev, irq)); 1101309312Sdexuan} 1102309312Sdexuan 1103309312Sdexuanstatic int 1104309312Sdexuanvmbus_map_msi(device_t bus, device_t dev, int irq, uint64_t *addr, 1105309312Sdexuan uint32_t *data) 1106309312Sdexuan{ 1107318392Ssephe 1108318392Ssephe return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data)); 1109309312Sdexuan} 1110309312Sdexuan 1111307302Ssephestatic uint32_t 1112307302Ssephevmbus_get_version_method(device_t bus, device_t dev) 1113307302Ssephe{ 1114307302Ssephe struct vmbus_softc *sc = device_get_softc(bus); 1115307302Ssephe 1116307302Ssephe return sc->vmbus_version; 1117307302Ssephe} 1118307302Ssephe 1119307291Ssephestatic int 1120307448Ssephevmbus_probe_guid_method(device_t bus, device_t dev, 1121307448Ssephe const struct hyperv_guid *guid) 1122307307Ssephe{ 1123307461Ssephe const struct vmbus_channel *chan = vmbus_get_channel(dev); 1124307307Ssephe 1125307448Ssephe if (memcmp(&chan->ch_guid_type, guid, sizeof(struct hyperv_guid)) == 0) 1126307307Ssephe return 0; 1127307307Ssephe return ENXIO; 1128307307Ssephe} 1129307307Ssephe 1130309312Sdexuanstatic uint32_t 1131309312Sdexuanvmbus_get_vcpu_id_method(device_t bus, device_t dev, int cpu) 1132309312Sdexuan{ 1133309312Sdexuan const struct vmbus_softc *sc = device_get_softc(bus); 1134309312Sdexuan 1135309312Sdexuan return (VMBUS_PCPU_GET(sc, vcpuid, cpu)); 1136309312Sdexuan} 1137309312Sdexuan 1138311368Ssephestatic struct taskqueue * 1139311368Ssephevmbus_get_eventtq_method(device_t bus, device_t dev __unused, int cpu) 1140311368Ssephe{ 1141311368Ssephe const struct vmbus_softc *sc = device_get_softc(bus); 1142311368Ssephe 1143311368Ssephe KASSERT(cpu >= 0 && cpu < mp_ncpus, ("invalid cpu%d", cpu)); 1144311368Ssephe return (VMBUS_PCPU_GET(sc, event_tq, cpu)); 1145311368Ssephe} 1146311368Ssephe 1147309312Sdexuan#ifdef NEW_PCIB 1148309312Sdexuan#define VTPM_BASE_ADDR 0xfed40000 1149309312Sdexuan#define FOUR_GB (1ULL << 32) 1150309312Sdexuan 1151309312Sdexuanenum parse_pass { parse_64, parse_32 }; 1152309312Sdexuan 1153309312Sdexuanstruct parse_context { 1154309312Sdexuan device_t vmbus_dev; 1155309312Sdexuan enum parse_pass pass; 1156309312Sdexuan}; 1157309312Sdexuan 1158309312Sdexuanstatic ACPI_STATUS 1159309312Sdexuanparse_crs(ACPI_RESOURCE *res, void *ctx) 1160309312Sdexuan{ 1161309312Sdexuan const struct parse_context *pc = ctx; 1162309312Sdexuan device_t vmbus_dev = pc->vmbus_dev; 1163309312Sdexuan 1164309312Sdexuan struct vmbus_softc *sc = device_get_softc(vmbus_dev); 1165309312Sdexuan UINT64 start, end; 1166309312Sdexuan 1167309312Sdexuan switch (res->Type) { 1168309312Sdexuan case ACPI_RESOURCE_TYPE_ADDRESS32: 1169309312Sdexuan start = res->Data.Address32.Address.Minimum; 1170309312Sdexuan end = res->Data.Address32.Address.Maximum; 1171309312Sdexuan break; 1172309312Sdexuan 1173309312Sdexuan case ACPI_RESOURCE_TYPE_ADDRESS64: 1174309312Sdexuan start = res->Data.Address64.Address.Minimum; 1175309312Sdexuan end = res->Data.Address64.Address.Maximum; 1176309312Sdexuan break; 1177309312Sdexuan 1178309312Sdexuan default: 1179309312Sdexuan /* Unused types. */ 1180309312Sdexuan return (AE_OK); 1181309312Sdexuan } 1182309312Sdexuan 1183309312Sdexuan /* 1184309312Sdexuan * We don't use <1MB addresses. 1185309312Sdexuan */ 1186309312Sdexuan if (end < 0x100000) 1187309312Sdexuan return (AE_OK); 1188309312Sdexuan 1189309312Sdexuan /* Don't conflict with vTPM. */ 1190309312Sdexuan if (end >= VTPM_BASE_ADDR && start < VTPM_BASE_ADDR) 1191309312Sdexuan end = VTPM_BASE_ADDR - 1; 1192309312Sdexuan 1193309312Sdexuan if ((pc->pass == parse_32 && start < FOUR_GB) || 1194309312Sdexuan (pc->pass == parse_64 && start >= FOUR_GB)) 1195309312Sdexuan pcib_host_res_decodes(&sc->vmbus_mmio_res, SYS_RES_MEMORY, 1196309312Sdexuan start, end, 0); 1197309312Sdexuan 1198309312Sdexuan return (AE_OK); 1199309312Sdexuan} 1200309312Sdexuan 1201309312Sdexuanstatic void 1202309312Sdexuanvmbus_get_crs(device_t dev, device_t vmbus_dev, enum parse_pass pass) 1203309312Sdexuan{ 1204309312Sdexuan struct parse_context pc; 1205309312Sdexuan ACPI_STATUS status; 1206309312Sdexuan 1207309312Sdexuan if (bootverbose) 1208309312Sdexuan device_printf(dev, "walking _CRS, pass=%d\n", pass); 1209309312Sdexuan 1210309312Sdexuan pc.vmbus_dev = vmbus_dev; 1211309312Sdexuan pc.pass = pass; 1212309312Sdexuan status = AcpiWalkResources(acpi_get_handle(dev), "_CRS", 1213309312Sdexuan parse_crs, &pc); 1214309312Sdexuan 1215309312Sdexuan if (bootverbose && ACPI_FAILURE(status)) 1216309312Sdexuan device_printf(dev, "_CRS: not found, pass=%d\n", pass); 1217309312Sdexuan} 1218309312Sdexuan 1219309312Sdexuanstatic void 1220309312Sdexuanvmbus_get_mmio_res_pass(device_t dev, enum parse_pass pass) 1221309312Sdexuan{ 1222318392Ssephe device_t acpi0, parent; 1223309312Sdexuan 1224318392Ssephe parent = device_get_parent(dev); 1225309312Sdexuan 1226318392Ssephe acpi0 = device_get_parent(parent); 1227318392Ssephe if (strcmp("acpi0", device_get_nameunit(acpi0)) == 0) { 1228318392Ssephe device_t *children; 1229318392Ssephe int count; 1230309312Sdexuan 1231318392Ssephe /* 1232318392Ssephe * Try to locate VMBUS resources and find _CRS on them. 1233318392Ssephe */ 1234318392Ssephe if (device_get_children(acpi0, &children, &count) == 0) { 1235318392Ssephe int i; 1236309312Sdexuan 1237318392Ssephe for (i = 0; i < count; ++i) { 1238318392Ssephe if (!device_is_attached(children[i])) 1239318392Ssephe continue; 1240309312Sdexuan 1241318392Ssephe if (strcmp("vmbus_res", 1242318392Ssephe device_get_name(children[i])) == 0) 1243318392Ssephe vmbus_get_crs(children[i], dev, pass); 1244318392Ssephe } 1245318392Ssephe free(children, M_TEMP); 1246318392Ssephe } 1247309312Sdexuan 1248318392Ssephe /* 1249318392Ssephe * Try to find _CRS on acpi. 1250318392Ssephe */ 1251318392Ssephe vmbus_get_crs(acpi0, dev, pass); 1252318392Ssephe } else { 1253318392Ssephe device_printf(dev, "not grandchild of acpi\n"); 1254309312Sdexuan } 1255309312Sdexuan 1256318392Ssephe /* 1257318392Ssephe * Try to find _CRS on parent. 1258318392Ssephe */ 1259318392Ssephe vmbus_get_crs(parent, dev, pass); 1260309312Sdexuan} 1261309312Sdexuan 1262309312Sdexuanstatic void 1263309312Sdexuanvmbus_get_mmio_res(device_t dev) 1264309312Sdexuan{ 1265309312Sdexuan struct vmbus_softc *sc = device_get_softc(dev); 1266309312Sdexuan /* 1267309312Sdexuan * We walk the resources twice to make sure that: in the resource 1268309312Sdexuan * list, the 32-bit resources appear behind the 64-bit resources. 1269309312Sdexuan * NB: resource_list_add() uses INSERT_TAIL. This way, when we 1270309312Sdexuan * iterate through the list to find a range for a 64-bit BAR in 1271309312Sdexuan * vmbus_alloc_resource(), we can make sure we try to use >4GB 1272309312Sdexuan * ranges first. 1273309312Sdexuan */ 1274309312Sdexuan pcib_host_res_init(dev, &sc->vmbus_mmio_res); 1275309312Sdexuan 1276309312Sdexuan vmbus_get_mmio_res_pass(dev, parse_64); 1277309312Sdexuan vmbus_get_mmio_res_pass(dev, parse_32); 1278309312Sdexuan} 1279309312Sdexuan 1280309312Sdexuanstatic void 1281309312Sdexuanvmbus_free_mmio_res(device_t dev) 1282309312Sdexuan{ 1283309312Sdexuan struct vmbus_softc *sc = device_get_softc(dev); 1284309312Sdexuan 1285309312Sdexuan pcib_host_res_free(dev, &sc->vmbus_mmio_res); 1286309312Sdexuan} 1287309312Sdexuan#endif /* NEW_PCIB */ 1288309312Sdexuan 1289318392Ssephestatic void 1290318392Ssephevmbus_identify(driver_t *driver, device_t parent) 1291318392Ssephe{ 1292318392Ssephe 1293318392Ssephe if (device_get_unit(parent) != 0 || vm_guest != VM_GUEST_HV || 1294318392Ssephe (hyperv_features & CPUID_HV_MSR_SYNIC) == 0) 1295318392Ssephe return; 1296318392Ssephe device_add_child(parent, "vmbus", -1); 1297318392Ssephe} 1298318392Ssephe 1299307307Ssephestatic int 1300300127Ssephevmbus_probe(device_t dev) 1301300127Ssephe{ 1302301018Ssephe 1303318392Ssephe if (device_get_unit(dev) != 0 || vm_guest != VM_GUEST_HV || 1304300834Ssephe (hyperv_features & CPUID_HV_MSR_SYNIC) == 0) 1305293870Ssephe return (ENXIO); 1306250199Sgrehan 1307300129Ssephe device_set_desc(dev, "Hyper-V Vmbus"); 1308293870Ssephe return (BUS_PROBE_DEFAULT); 1309250199Sgrehan} 1310250199Sgrehan 1311282212Swhu/** 1312250199Sgrehan * @brief Main vmbus driver initialization routine. 1313250199Sgrehan * 1314250199Sgrehan * Here, we 1315250199Sgrehan * - initialize the vmbus driver context 1316250199Sgrehan * - setup various driver entry points 1317250199Sgrehan * - invoke the vmbus hv main init routine 1318250199Sgrehan * - get the irq resource 1319250199Sgrehan * - invoke the vmbus to add the vmbus root device 1320250199Sgrehan * - setup the vmbus root device 1321250199Sgrehan * - retrieve the channel offers 1322250199Sgrehan */ 1323250199Sgrehanstatic int 1324307291Ssephevmbus_doattach(struct vmbus_softc *sc) 1325250199Sgrehan{ 1326307291Ssephe struct sysctl_oid_list *child; 1327307291Ssephe struct sysctl_ctx_list *ctx; 1328300574Ssephe int ret; 1329250199Sgrehan 1330300650Ssephe if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED) 1331250199Sgrehan return (0); 1332309312Sdexuan 1333309312Sdexuan#ifdef NEW_PCIB 1334309312Sdexuan vmbus_get_mmio_res(sc->vmbus_dev); 1335309312Sdexuan#endif 1336309312Sdexuan 1337300650Ssephe sc->vmbus_flags |= VMBUS_FLAG_ATTACHED; 1338250199Sgrehan 1339307302Ssephe sc->vmbus_gpadl = VMBUS_GPADL_START; 1340307450Ssephe mtx_init(&sc->vmbus_prichan_lock, "vmbus prichan", NULL, MTX_DEF); 1341307450Ssephe TAILQ_INIT(&sc->vmbus_prichans); 1342307599Ssephe mtx_init(&sc->vmbus_chan_lock, "vmbus channel", NULL, MTX_DEF); 1343307599Ssephe TAILQ_INIT(&sc->vmbus_chans); 1344307304Ssephe sc->vmbus_chmap = malloc( 1345307461Ssephe sizeof(struct vmbus_channel *) * VMBUS_CHAN_MAX, M_DEVBUF, 1346307304Ssephe M_WAITOK | M_ZERO); 1347307291Ssephe 1348250199Sgrehan /* 1349307278Ssephe * Create context for "post message" Hypercalls 1350307278Ssephe */ 1351307475Ssephe sc->vmbus_xc = vmbus_xact_ctx_create(bus_get_dma_tag(sc->vmbus_dev), 1352307475Ssephe HYPERCALL_POSTMSGIN_SIZE, VMBUS_MSG_SIZE, 1353307475Ssephe sizeof(struct vmbus_msghc)); 1354307475Ssephe if (sc->vmbus_xc == NULL) { 1355307278Ssephe ret = ENXIO; 1356307278Ssephe goto cleanup; 1357307278Ssephe } 1358307278Ssephe 1359307278Ssephe /* 1360300645Ssephe * Allocate DMA stuffs. 1361250199Sgrehan */ 1362300645Ssephe ret = vmbus_dma_alloc(sc); 1363300574Ssephe if (ret != 0) 1364282212Swhu goto cleanup; 1365250199Sgrehan 1366250199Sgrehan /* 1367300645Ssephe * Setup interrupt. 1368250199Sgrehan */ 1369300645Ssephe ret = vmbus_intr_setup(sc); 1370300644Ssephe if (ret != 0) 1371300644Ssephe goto cleanup; 1372300572Ssephe 1373300650Ssephe /* 1374300650Ssephe * Setup SynIC. 1375300650Ssephe */ 1376282212Swhu if (bootverbose) 1377300650Ssephe device_printf(sc->vmbus_dev, "smp_started = %d\n", smp_started); 1378300652Ssephe smp_rendezvous(NULL, vmbus_synic_setup, NULL, sc); 1379300650Ssephe sc->vmbus_flags |= VMBUS_FLAG_SYNIC; 1380282212Swhu 1381255414Sgrehan /* 1382307304Ssephe * Initialize vmbus, e.g. connect to Hypervisor. 1383250199Sgrehan */ 1384307278Ssephe ret = vmbus_init(sc); 1385307278Ssephe if (ret != 0) 1386307278Ssephe goto cleanup; 1387307278Ssephe 1388307302Ssephe if (sc->vmbus_version == VMBUS_VERSION_WS2008 || 1389307302Ssephe sc->vmbus_version == VMBUS_VERSION_WIN7) 1390300107Ssephe sc->vmbus_event_proc = vmbus_event_proc_compat; 1391300107Ssephe else 1392300107Ssephe sc->vmbus_event_proc = vmbus_event_proc; 1393300107Ssephe 1394307291Ssephe ret = vmbus_scan(sc); 1395307291Ssephe if (ret != 0) 1396307291Ssephe goto cleanup; 1397298260Ssephe 1398307291Ssephe ctx = device_get_sysctl_ctx(sc->vmbus_dev); 1399307291Ssephe child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->vmbus_dev)); 1400307291Ssephe SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "version", 1401307302Ssephe CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 1402307291Ssephe vmbus_sysctl_version, "A", "vmbus version"); 1403298260Ssephe 1404250199Sgrehan return (ret); 1405250199Sgrehan 1406300574Ssephecleanup: 1407307599Ssephe vmbus_scan_teardown(sc); 1408300645Ssephe vmbus_intr_teardown(sc); 1409300572Ssephe vmbus_dma_free(sc); 1410307475Ssephe if (sc->vmbus_xc != NULL) { 1411307475Ssephe vmbus_xact_ctx_destroy(sc->vmbus_xc); 1412307475Ssephe sc->vmbus_xc = NULL; 1413307278Ssephe } 1414311389Ssephe free(__DEVOLATILE(void *, sc->vmbus_chmap), M_DEVBUF); 1415307450Ssephe mtx_destroy(&sc->vmbus_prichan_lock); 1416307599Ssephe mtx_destroy(&sc->vmbus_chan_lock); 1417250199Sgrehan 1418250199Sgrehan return (ret); 1419250199Sgrehan} 1420250199Sgrehan 1421300107Ssephestatic void 1422300107Ssephevmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused) 1423300107Ssephe{ 1424300107Ssephe} 1425300107Ssephe 1426310573Ssephe#ifdef EARLY_AP_STARTUP 1427310573Ssephe 1428310573Ssephestatic void 1429310573Ssephevmbus_intrhook(void *xsc) 1430310573Ssephe{ 1431310573Ssephe struct vmbus_softc *sc = xsc; 1432310573Ssephe 1433310573Ssephe if (bootverbose) 1434310573Ssephe device_printf(sc->vmbus_dev, "intrhook\n"); 1435310573Ssephe vmbus_doattach(sc); 1436310573Ssephe config_intrhook_disestablish(&sc->vmbus_intrhook); 1437310573Ssephe} 1438310573Ssephe 1439310573Ssephe#endif /* EARLY_AP_STARTUP */ 1440310573Ssephe 1441250199Sgrehanstatic int 1442250199Sgrehanvmbus_attach(device_t dev) 1443250199Sgrehan{ 1444300102Ssephe vmbus_sc = device_get_softc(dev); 1445300486Ssephe vmbus_sc->vmbus_dev = dev; 1446300574Ssephe vmbus_sc->vmbus_idtvec = -1; 1447250199Sgrehan 1448300107Ssephe /* 1449300107Ssephe * Event processing logic will be configured: 1450300107Ssephe * - After the vmbus protocol version negotiation. 1451300107Ssephe * - Before we request channel offers. 1452300107Ssephe */ 1453300107Ssephe vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy; 1454300107Ssephe 1455310573Ssephe#ifdef EARLY_AP_STARTUP 1456310573Ssephe /* 1457310573Ssephe * Defer the real attach until the pause(9) works as expected. 1458310573Ssephe */ 1459310573Ssephe vmbus_sc->vmbus_intrhook.ich_func = vmbus_intrhook; 1460310573Ssephe vmbus_sc->vmbus_intrhook.ich_arg = vmbus_sc; 1461310573Ssephe config_intrhook_establish(&vmbus_sc->vmbus_intrhook); 1462310573Ssephe#else /* !EARLY_AP_STARTUP */ 1463250199Sgrehan /* 1464250199Sgrehan * If the system has already booted and thread 1465250199Sgrehan * scheduling is possible indicated by the global 1466250199Sgrehan * cold set to zero, we just call the driver 1467250199Sgrehan * initialization directly. 1468250199Sgrehan */ 1469250199Sgrehan if (!cold) 1470307291Ssephe vmbus_doattach(vmbus_sc); 1471310573Ssephe#endif /* EARLY_AP_STARTUP */ 1472250199Sgrehan 1473250199Sgrehan return (0); 1474250199Sgrehan} 1475250199Sgrehan 1476300121Ssephestatic int 1477300121Ssephevmbus_detach(device_t dev) 1478250199Sgrehan{ 1479300487Ssephe struct vmbus_softc *sc = device_get_softc(dev); 1480255414Sgrehan 1481307599Ssephe bus_generic_detach(dev); 1482307450Ssephe vmbus_chan_destroy_all(sc); 1483307291Ssephe 1484307599Ssephe vmbus_scan_teardown(sc); 1485307599Ssephe 1486307302Ssephe vmbus_disconnect(sc); 1487250199Sgrehan 1488300650Ssephe if (sc->vmbus_flags & VMBUS_FLAG_SYNIC) { 1489300650Ssephe sc->vmbus_flags &= ~VMBUS_FLAG_SYNIC; 1490300650Ssephe smp_rendezvous(NULL, vmbus_synic_teardown, NULL, NULL); 1491300650Ssephe } 1492250199Sgrehan 1493300645Ssephe vmbus_intr_teardown(sc); 1494300572Ssephe vmbus_dma_free(sc); 1495255414Sgrehan 1496307475Ssephe if (sc->vmbus_xc != NULL) { 1497307475Ssephe vmbus_xact_ctx_destroy(sc->vmbus_xc); 1498307475Ssephe sc->vmbus_xc = NULL; 1499307278Ssephe } 1500307278Ssephe 1501311389Ssephe free(__DEVOLATILE(void *, sc->vmbus_chmap), M_DEVBUF); 1502307450Ssephe mtx_destroy(&sc->vmbus_prichan_lock); 1503307599Ssephe mtx_destroy(&sc->vmbus_chan_lock); 1504307304Ssephe 1505309312Sdexuan#ifdef NEW_PCIB 1506309312Sdexuan vmbus_free_mmio_res(dev); 1507309312Sdexuan#endif 1508309312Sdexuan 1509250199Sgrehan return (0); 1510250199Sgrehan} 1511250199Sgrehan 1512307466Ssephe#ifndef EARLY_AP_STARTUP 1513250199Sgrehan 1514307466Ssephestatic void 1515307466Ssephevmbus_sysinit(void *arg __unused) 1516307466Ssephe{ 1517307466Ssephe struct vmbus_softc *sc = vmbus_get_softc(); 1518250199Sgrehan 1519307466Ssephe if (vm_guest != VM_GUEST_HV || sc == NULL) 1520307466Ssephe return; 1521307302Ssephe 1522307466Ssephe /* 1523307466Ssephe * If the system has already booted and thread 1524307466Ssephe * scheduling is possible, as indicated by the 1525307466Ssephe * global cold set to zero, we just call the driver 1526307466Ssephe * initialization directly. 1527307466Ssephe */ 1528307466Ssephe if (!cold) 1529307466Ssephe vmbus_doattach(sc); 1530307466Ssephe} 1531300126Ssephe/* 1532300126Ssephe * NOTE: 1533300126Ssephe * We have to start as the last step of SI_SUB_SMP, i.e. after SMP is 1534300126Ssephe * initialized. 1535300126Ssephe */ 1536300126SsepheSYSINIT(vmbus_initialize, SI_SUB_SMP, SI_ORDER_ANY, vmbus_sysinit, NULL); 1537307466Ssephe 1538307466Ssephe#endif /* !EARLY_AP_STARTUP */ 1539