hn_nvs.c revision 314123
1250199Sgrehan/*- 2302045Ssephe * Copyright (c) 2009-2012,2016 Microsoft Corp. 3250199Sgrehan * Copyright (c) 2010-2012 Citrix Inc. 4250199Sgrehan * Copyright (c) 2012 NetApp 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 29308504Ssephe/* 30308504Ssephe * Network Virtualization Service. 31250199Sgrehan */ 32250199Sgrehan 33308504Ssephe#include <sys/cdefs.h> 34308504Ssephe__FBSDID("$FreeBSD: stable/10/sys/dev/hyperv/netvsc/hn_nvs.c 314123 2017-02-23 07:04:17Z dexuan $"); 35250199Sgrehan 36308504Ssephe#include "opt_inet6.h" 37308504Ssephe#include "opt_inet.h" 38308504Ssephe 39250199Sgrehan#include <sys/param.h> 40250199Sgrehan#include <sys/kernel.h> 41308504Ssephe#include <sys/limits.h> 42250199Sgrehan#include <sys/socket.h> 43308504Ssephe#include <sys/systm.h> 44308504Ssephe#include <sys/taskqueue.h> 45308504Ssephe 46250199Sgrehan#include <net/if.h> 47250199Sgrehan#include <net/if_arp.h> 48308504Ssephe#include <net/if_media.h> 49250199Sgrehan 50308504Ssephe#include <netinet/in.h> 51308504Ssephe#include <netinet/tcp_lro.h> 52308504Ssephe 53250199Sgrehan#include <dev/hyperv/include/hyperv.h> 54308504Ssephe#include <dev/hyperv/include/hyperv_busdma.h> 55308504Ssephe#include <dev/hyperv/include/vmbus.h> 56307164Ssephe#include <dev/hyperv/include/vmbus_xact.h> 57308504Ssephe 58308504Ssephe#include <dev/hyperv/netvsc/ndis.h> 59307164Ssephe#include <dev/hyperv/netvsc/if_hnreg.h> 60307199Ssephe#include <dev/hyperv/netvsc/if_hnvar.h> 61308505Ssephe#include <dev/hyperv/netvsc/hn_nvs.h> 62250199Sgrehan 63308504Ssephestatic int hn_nvs_conn_chim(struct hn_softc *); 64308504Ssephestatic int hn_nvs_conn_rxbuf(struct hn_softc *); 65310757Ssephestatic void hn_nvs_disconn_chim(struct hn_softc *); 66310757Ssephestatic void hn_nvs_disconn_rxbuf(struct hn_softc *); 67308504Ssephestatic int hn_nvs_conf_ndis(struct hn_softc *, int); 68308504Ssephestatic int hn_nvs_init_ndis(struct hn_softc *); 69308504Ssephestatic int hn_nvs_doinit(struct hn_softc *, uint32_t); 70308504Ssephestatic int hn_nvs_init(struct hn_softc *); 71308504Ssephestatic const void *hn_nvs_xact_execute(struct hn_softc *, 72308504Ssephe struct vmbus_xact *, void *, int, 73308504Ssephe size_t *, uint32_t); 74308504Ssephestatic void hn_nvs_sent_none(struct hn_nvs_sendctx *, 75308504Ssephe struct hn_softc *, struct vmbus_channel *, 76308504Ssephe const void *, int); 77250199Sgrehan 78308504Ssephestruct hn_nvs_sendctx hn_nvs_sendctx_none = 79308504Ssephe HN_NVS_SENDCTX_INITIALIZER(hn_nvs_sent_none, NULL); 80307161Ssephe 81307200Ssephestatic const uint32_t hn_nvs_version[] = { 82307200Ssephe HN_NVS_VERSION_5, 83307200Ssephe HN_NVS_VERSION_4, 84307200Ssephe HN_NVS_VERSION_2, 85307200Ssephe HN_NVS_VERSION_1 86307200Ssephe}; 87307200Ssephe 88307206Ssephestatic const void * 89307174Ssephehn_nvs_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, 90307186Ssephe void *req, int reqlen, size_t *resplen0, uint32_t type) 91285236Swhu{ 92308504Ssephe struct hn_nvs_sendctx sndc; 93307186Ssephe size_t resplen, min_resplen = *resplen0; 94307186Ssephe const struct hn_nvs_hdr *hdr; 95307174Ssephe int error; 96285236Swhu 97307186Ssephe KASSERT(min_resplen >= sizeof(*hdr), 98307186Ssephe ("invalid minimum response len %zu", min_resplen)); 99307186Ssephe 100307186Ssephe /* 101307186Ssephe * Execute the xact setup by the caller. 102307186Ssephe */ 103308504Ssephe hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact); 104307186Ssephe 105307174Ssephe vmbus_xact_activate(xact); 106307174Ssephe error = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_RC, 107307174Ssephe req, reqlen, &sndc); 108307174Ssephe if (error) { 109307174Ssephe vmbus_xact_deactivate(xact); 110307186Ssephe return (NULL); 111307174Ssephe } 112310757Ssephe hdr = vmbus_chan_xact_wait(sc->hn_prichan, xact, &resplen, 113310757Ssephe HN_CAN_SLEEP(sc)); 114307186Ssephe 115307186Ssephe /* 116307186Ssephe * Check this NVS response message. 117307186Ssephe */ 118307186Ssephe if (resplen < min_resplen) { 119307186Ssephe if_printf(sc->hn_ifp, "invalid NVS resp len %zu\n", resplen); 120307186Ssephe return (NULL); 121307186Ssephe } 122307186Ssephe if (hdr->nvs_type != type) { 123307186Ssephe if_printf(sc->hn_ifp, "unexpected NVS resp 0x%08x, " 124307186Ssephe "expect 0x%08x\n", hdr->nvs_type, type); 125307186Ssephe return (NULL); 126307186Ssephe } 127307186Ssephe /* All pass! */ 128307186Ssephe *resplen0 = resplen; 129307186Ssephe return (hdr); 130307174Ssephe} 131285236Swhu 132307174Ssephestatic __inline int 133307174Ssephehn_nvs_req_send(struct hn_softc *sc, void *req, int reqlen) 134307174Ssephe{ 135285236Swhu 136307174Ssephe return (hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_NONE, 137308504Ssephe req, reqlen, &hn_nvs_sendctx_none)); 138285236Swhu} 139285236Swhu 140250199Sgrehanstatic int 141307206Ssephehn_nvs_conn_rxbuf(struct hn_softc *sc) 142250199Sgrehan{ 143307174Ssephe struct vmbus_xact *xact = NULL; 144307164Ssephe struct hn_nvs_rxbuf_conn *conn; 145307164Ssephe const struct hn_nvs_rxbuf_connresp *resp; 146307164Ssephe size_t resp_len; 147307164Ssephe uint32_t status; 148307200Ssephe int error, rxbuf_size; 149250199Sgrehan 150307200Ssephe /* 151307200Ssephe * Limit RXBUF size for old NVS. 152307200Ssephe */ 153307200Ssephe if (sc->hn_nvs_ver <= HN_NVS_VERSION_2) 154308504Ssephe rxbuf_size = HN_RXBUF_SIZE_COMPAT; 155307200Ssephe else 156308504Ssephe rxbuf_size = HN_RXBUF_SIZE; 157250199Sgrehan 158250199Sgrehan /* 159307081Ssephe * Connect the RXBUF GPADL to the primary channel. 160307081Ssephe * 161307081Ssephe * NOTE: 162307081Ssephe * Only primary channel has RXBUF connected to it. Sub-channels 163307081Ssephe * just share this RXBUF. 164250199Sgrehan */ 165307164Ssephe error = vmbus_chan_gpadl_connect(sc->hn_prichan, 166307174Ssephe sc->hn_rxbuf_dma.hv_paddr, rxbuf_size, &sc->hn_rxbuf_gpadl); 167307164Ssephe if (error) { 168307206Ssephe if_printf(sc->hn_ifp, "rxbuf gpadl conn failed: %d\n", 169307164Ssephe error); 170250199Sgrehan goto cleanup; 171250199Sgrehan } 172250199Sgrehan 173307164Ssephe /* 174307164Ssephe * Connect RXBUF to NVS. 175307164Ssephe */ 176250199Sgrehan 177307164Ssephe xact = vmbus_xact_get(sc->hn_xact, sizeof(*conn)); 178307164Ssephe if (xact == NULL) { 179307164Ssephe if_printf(sc->hn_ifp, "no xact for nvs rxbuf conn\n"); 180307164Ssephe error = ENXIO; 181307164Ssephe goto cleanup; 182307164Ssephe } 183307164Ssephe conn = vmbus_xact_req_data(xact); 184307164Ssephe conn->nvs_type = HN_NVS_TYPE_RXBUF_CONN; 185307174Ssephe conn->nvs_gpadl = sc->hn_rxbuf_gpadl; 186307164Ssephe conn->nvs_sig = HN_NVS_RXBUF_SIG; 187250199Sgrehan 188307186Ssephe resp_len = sizeof(*resp); 189307186Ssephe resp = hn_nvs_xact_execute(sc, xact, conn, sizeof(*conn), &resp_len, 190307186Ssephe HN_NVS_TYPE_RXBUF_CONNRESP); 191307174Ssephe if (resp == NULL) { 192307206Ssephe if_printf(sc->hn_ifp, "exec nvs rxbuf conn failed\n"); 193307174Ssephe error = EIO; 194250199Sgrehan goto cleanup; 195250199Sgrehan } 196250199Sgrehan 197307164Ssephe status = resp->nvs_status; 198307164Ssephe vmbus_xact_put(xact); 199307174Ssephe xact = NULL; 200250199Sgrehan 201307164Ssephe if (status != HN_NVS_STATUS_OK) { 202307206Ssephe if_printf(sc->hn_ifp, "nvs rxbuf conn failed: %x\n", status); 203307164Ssephe error = EIO; 204250199Sgrehan goto cleanup; 205250199Sgrehan } 206307174Ssephe sc->hn_flags |= HN_FLAG_RXBUF_CONNECTED; 207250199Sgrehan 208307164Ssephe return (0); 209250199Sgrehan 210250199Sgrehancleanup: 211307174Ssephe if (xact != NULL) 212307174Ssephe vmbus_xact_put(xact); 213307206Ssephe hn_nvs_disconn_rxbuf(sc); 214307164Ssephe return (error); 215250199Sgrehan} 216250199Sgrehan 217250199Sgrehanstatic int 218307206Ssephehn_nvs_conn_chim(struct hn_softc *sc) 219250199Sgrehan{ 220307174Ssephe struct vmbus_xact *xact = NULL; 221307164Ssephe struct hn_nvs_chim_conn *chim; 222307164Ssephe const struct hn_nvs_chim_connresp *resp; 223307164Ssephe size_t resp_len; 224307164Ssephe uint32_t status, sectsz; 225307164Ssephe int error; 226250199Sgrehan 227250199Sgrehan /* 228307081Ssephe * Connect chimney sending buffer GPADL to the primary channel. 229307081Ssephe * 230307081Ssephe * NOTE: 231307081Ssephe * Only primary channel has chimney sending buffer connected to it. 232307081Ssephe * Sub-channels just share this chimney sending buffer. 233250199Sgrehan */ 234307164Ssephe error = vmbus_chan_gpadl_connect(sc->hn_prichan, 235308504Ssephe sc->hn_chim_dma.hv_paddr, HN_CHIM_SIZE, &sc->hn_chim_gpadl); 236307164Ssephe if (error) { 237307206Ssephe if_printf(sc->hn_ifp, "chim gpadl conn failed: %d\n", error); 238250199Sgrehan goto cleanup; 239250199Sgrehan } 240250199Sgrehan 241307164Ssephe /* 242307164Ssephe * Connect chimney sending buffer to NVS 243307164Ssephe */ 244250199Sgrehan 245307164Ssephe xact = vmbus_xact_get(sc->hn_xact, sizeof(*chim)); 246307164Ssephe if (xact == NULL) { 247307164Ssephe if_printf(sc->hn_ifp, "no xact for nvs chim conn\n"); 248307164Ssephe error = ENXIO; 249307164Ssephe goto cleanup; 250307164Ssephe } 251307164Ssephe chim = vmbus_xact_req_data(xact); 252307164Ssephe chim->nvs_type = HN_NVS_TYPE_CHIM_CONN; 253307174Ssephe chim->nvs_gpadl = sc->hn_chim_gpadl; 254307164Ssephe chim->nvs_sig = HN_NVS_CHIM_SIG; 255250199Sgrehan 256307186Ssephe resp_len = sizeof(*resp); 257307186Ssephe resp = hn_nvs_xact_execute(sc, xact, chim, sizeof(*chim), &resp_len, 258307186Ssephe HN_NVS_TYPE_CHIM_CONNRESP); 259307174Ssephe if (resp == NULL) { 260307206Ssephe if_printf(sc->hn_ifp, "exec nvs chim conn failed\n"); 261307174Ssephe error = EIO; 262250199Sgrehan goto cleanup; 263250199Sgrehan } 264250199Sgrehan 265307164Ssephe status = resp->nvs_status; 266307164Ssephe sectsz = resp->nvs_sectsz; 267307164Ssephe vmbus_xact_put(xact); 268307174Ssephe xact = NULL; 269307164Ssephe 270307164Ssephe if (status != HN_NVS_STATUS_OK) { 271307206Ssephe if_printf(sc->hn_ifp, "nvs chim conn failed: %x\n", status); 272307164Ssephe error = EIO; 273250199Sgrehan goto cleanup; 274250199Sgrehan } 275307164Ssephe if (sectsz == 0) { 276310757Ssephe /* 277310757Ssephe * Can't use chimney sending buffer; done! 278310757Ssephe */ 279307164Ssephe if_printf(sc->hn_ifp, "zero chimney sending buffer " 280307164Ssephe "section size\n"); 281310757Ssephe sc->hn_chim_szmax = 0; 282310757Ssephe sc->hn_chim_cnt = 0; 283310757Ssephe sc->hn_flags |= HN_FLAG_CHIM_CONNECTED; 284307206Ssephe return (0); 285307164Ssephe } 286250199Sgrehan 287307174Ssephe sc->hn_chim_szmax = sectsz; 288308504Ssephe sc->hn_chim_cnt = HN_CHIM_SIZE / sc->hn_chim_szmax; 289308504Ssephe if (HN_CHIM_SIZE % sc->hn_chim_szmax != 0) { 290307174Ssephe if_printf(sc->hn_ifp, "chimney sending sections are " 291307174Ssephe "not properly aligned\n"); 292307174Ssephe } 293307174Ssephe if (sc->hn_chim_cnt % LONG_BIT != 0) { 294307174Ssephe if_printf(sc->hn_ifp, "discard %d chimney sending sections\n", 295307174Ssephe sc->hn_chim_cnt % LONG_BIT); 296307174Ssephe } 297250199Sgrehan 298307174Ssephe sc->hn_chim_bmap_cnt = sc->hn_chim_cnt / LONG_BIT; 299307174Ssephe sc->hn_chim_bmap = malloc(sc->hn_chim_bmap_cnt * sizeof(u_long), 300308503Ssephe M_DEVBUF, M_WAITOK | M_ZERO); 301307174Ssephe 302307174Ssephe /* Done! */ 303307174Ssephe sc->hn_flags |= HN_FLAG_CHIM_CONNECTED; 304307164Ssephe if (bootverbose) { 305307174Ssephe if_printf(sc->hn_ifp, "chimney sending buffer %d/%d\n", 306307174Ssephe sc->hn_chim_szmax, sc->hn_chim_cnt); 307307164Ssephe } 308307206Ssephe return (0); 309250199Sgrehan 310250199Sgrehancleanup: 311307174Ssephe if (xact != NULL) 312307174Ssephe vmbus_xact_put(xact); 313307206Ssephe hn_nvs_disconn_chim(sc); 314307164Ssephe return (error); 315250199Sgrehan} 316250199Sgrehan 317310757Ssephestatic void 318307206Ssephehn_nvs_disconn_rxbuf(struct hn_softc *sc) 319250199Sgrehan{ 320307206Ssephe int error; 321250199Sgrehan 322307174Ssephe if (sc->hn_flags & HN_FLAG_RXBUF_CONNECTED) { 323307164Ssephe struct hn_nvs_rxbuf_disconn disconn; 324250199Sgrehan 325307164Ssephe /* 326307164Ssephe * Disconnect RXBUF from NVS. 327307164Ssephe */ 328307164Ssephe memset(&disconn, 0, sizeof(disconn)); 329307164Ssephe disconn.nvs_type = HN_NVS_TYPE_RXBUF_DISCONN; 330307164Ssephe disconn.nvs_sig = HN_NVS_RXBUF_SIG; 331250199Sgrehan 332307167Ssephe /* NOTE: No response. */ 333307206Ssephe error = hn_nvs_req_send(sc, &disconn, sizeof(disconn)); 334307206Ssephe if (error) { 335307174Ssephe if_printf(sc->hn_ifp, 336307206Ssephe "send nvs rxbuf disconn failed: %d\n", error); 337310757Ssephe /* 338310757Ssephe * Fine for a revoked channel, since the hypervisor 339310757Ssephe * does not drain TX bufring for a revoked channel. 340310757Ssephe */ 341310757Ssephe if (!vmbus_chan_is_revoked(sc->hn_prichan)) 342310757Ssephe sc->hn_flags |= HN_FLAG_RXBUF_REF; 343250199Sgrehan } 344307174Ssephe sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED; 345307250Ssephe 346307250Ssephe /* 347307250Ssephe * Wait for the hypervisor to receive this NVS request. 348310743Ssephe * 349310743Ssephe * NOTE: 350310743Ssephe * The TX bufring will not be drained by the hypervisor, 351310743Ssephe * if the primary channel is revoked. 352307250Ssephe */ 353310743Ssephe while (!vmbus_chan_tx_empty(sc->hn_prichan) && 354310743Ssephe !vmbus_chan_is_revoked(sc->hn_prichan)) 355307250Ssephe pause("waittx", 1); 356307250Ssephe /* 357307250Ssephe * Linger long enough for NVS to disconnect RXBUF. 358307250Ssephe */ 359307250Ssephe pause("lingtx", (200 * hz) / 1000); 360250199Sgrehan } 361307206Ssephe 362307174Ssephe if (sc->hn_rxbuf_gpadl != 0) { 363250199Sgrehan /* 364307174Ssephe * Disconnect RXBUF from primary channel. 365250199Sgrehan */ 366307206Ssephe error = vmbus_chan_gpadl_disconnect(sc->hn_prichan, 367307174Ssephe sc->hn_rxbuf_gpadl); 368307206Ssephe if (error) { 369307174Ssephe if_printf(sc->hn_ifp, 370307206Ssephe "rxbuf gpadl disconn failed: %d\n", error); 371310757Ssephe sc->hn_flags |= HN_FLAG_RXBUF_REF; 372250199Sgrehan } 373307174Ssephe sc->hn_rxbuf_gpadl = 0; 374250199Sgrehan } 375250199Sgrehan} 376250199Sgrehan 377310757Ssephestatic void 378307206Ssephehn_nvs_disconn_chim(struct hn_softc *sc) 379250199Sgrehan{ 380307206Ssephe int error; 381250199Sgrehan 382307174Ssephe if (sc->hn_flags & HN_FLAG_CHIM_CONNECTED) { 383307164Ssephe struct hn_nvs_chim_disconn disconn; 384250199Sgrehan 385307164Ssephe /* 386307164Ssephe * Disconnect chimney sending buffer from NVS. 387307164Ssephe */ 388307164Ssephe memset(&disconn, 0, sizeof(disconn)); 389307164Ssephe disconn.nvs_type = HN_NVS_TYPE_CHIM_DISCONN; 390307164Ssephe disconn.nvs_sig = HN_NVS_CHIM_SIG; 391250199Sgrehan 392307167Ssephe /* NOTE: No response. */ 393307206Ssephe error = hn_nvs_req_send(sc, &disconn, sizeof(disconn)); 394307206Ssephe if (error) { 395307174Ssephe if_printf(sc->hn_ifp, 396307206Ssephe "send nvs chim disconn failed: %d\n", error); 397310757Ssephe /* 398310757Ssephe * Fine for a revoked channel, since the hypervisor 399310757Ssephe * does not drain TX bufring for a revoked channel. 400310757Ssephe */ 401310757Ssephe if (!vmbus_chan_is_revoked(sc->hn_prichan)) 402310757Ssephe sc->hn_flags |= HN_FLAG_CHIM_REF; 403250199Sgrehan } 404307174Ssephe sc->hn_flags &= ~HN_FLAG_CHIM_CONNECTED; 405307250Ssephe 406307250Ssephe /* 407307250Ssephe * Wait for the hypervisor to receive this NVS request. 408310743Ssephe * 409310743Ssephe * NOTE: 410310743Ssephe * The TX bufring will not be drained by the hypervisor, 411310743Ssephe * if the primary channel is revoked. 412307250Ssephe */ 413310743Ssephe while (!vmbus_chan_tx_empty(sc->hn_prichan) && 414310743Ssephe !vmbus_chan_is_revoked(sc->hn_prichan)) 415307250Ssephe pause("waittx", 1); 416307250Ssephe /* 417307250Ssephe * Linger long enough for NVS to disconnect chimney 418307250Ssephe * sending buffer. 419307250Ssephe */ 420307250Ssephe pause("lingtx", (200 * hz) / 1000); 421250199Sgrehan } 422307206Ssephe 423307174Ssephe if (sc->hn_chim_gpadl != 0) { 424250199Sgrehan /* 425307174Ssephe * Disconnect chimney sending buffer from primary channel. 426250199Sgrehan */ 427307206Ssephe error = vmbus_chan_gpadl_disconnect(sc->hn_prichan, 428307174Ssephe sc->hn_chim_gpadl); 429307206Ssephe if (error) { 430307174Ssephe if_printf(sc->hn_ifp, 431307206Ssephe "chim gpadl disconn failed: %d\n", error); 432310757Ssephe sc->hn_flags |= HN_FLAG_CHIM_REF; 433250199Sgrehan } 434307174Ssephe sc->hn_chim_gpadl = 0; 435250199Sgrehan } 436250199Sgrehan 437307174Ssephe if (sc->hn_chim_bmap != NULL) { 438308503Ssephe free(sc->hn_chim_bmap, M_DEVBUF); 439307174Ssephe sc->hn_chim_bmap = NULL; 440310757Ssephe sc->hn_chim_bmap_cnt = 0; 441250199Sgrehan } 442250199Sgrehan} 443250199Sgrehan 444250199Sgrehanstatic int 445307200Ssephehn_nvs_doinit(struct hn_softc *sc, uint32_t nvs_ver) 446250199Sgrehan{ 447307164Ssephe struct vmbus_xact *xact; 448307164Ssephe struct hn_nvs_init *init; 449307164Ssephe const struct hn_nvs_init_resp *resp; 450307164Ssephe size_t resp_len; 451307164Ssephe uint32_t status; 452250199Sgrehan 453307164Ssephe xact = vmbus_xact_get(sc->hn_xact, sizeof(*init)); 454307164Ssephe if (xact == NULL) { 455307164Ssephe if_printf(sc->hn_ifp, "no xact for nvs init\n"); 456307164Ssephe return (ENXIO); 457307164Ssephe } 458307164Ssephe init = vmbus_xact_req_data(xact); 459307164Ssephe init->nvs_type = HN_NVS_TYPE_INIT; 460307164Ssephe init->nvs_ver_min = nvs_ver; 461307164Ssephe init->nvs_ver_max = nvs_ver; 462250199Sgrehan 463307186Ssephe resp_len = sizeof(*resp); 464307186Ssephe resp = hn_nvs_xact_execute(sc, xact, init, sizeof(*init), &resp_len, 465307186Ssephe HN_NVS_TYPE_INIT_RESP); 466307174Ssephe if (resp == NULL) { 467307174Ssephe if_printf(sc->hn_ifp, "exec init failed\n"); 468307164Ssephe vmbus_xact_put(xact); 469307174Ssephe return (EIO); 470307164Ssephe } 471250199Sgrehan 472307164Ssephe status = resp->nvs_status; 473307164Ssephe vmbus_xact_put(xact); 474307164Ssephe 475307164Ssephe if (status != HN_NVS_STATUS_OK) { 476307246Ssephe if (bootverbose) { 477307246Ssephe /* 478307246Ssephe * Caller may try another NVS version, and will log 479307246Ssephe * error if there are no more NVS versions to try, 480307246Ssephe * so don't bark out loud here. 481307246Ssephe */ 482307246Ssephe if_printf(sc->hn_ifp, "nvs init failed for ver 0x%x\n", 483307246Ssephe nvs_ver); 484307246Ssephe } 485250199Sgrehan return (EINVAL); 486307164Ssephe } 487250199Sgrehan return (0); 488250199Sgrehan} 489250199Sgrehan 490250199Sgrehan/* 491307202Ssephe * Configure MTU and enable VLAN. 492250199Sgrehan */ 493250199Sgrehanstatic int 494307202Ssephehn_nvs_conf_ndis(struct hn_softc *sc, int mtu) 495250199Sgrehan{ 496307164Ssephe struct hn_nvs_ndis_conf conf; 497307164Ssephe int error; 498250199Sgrehan 499307164Ssephe memset(&conf, 0, sizeof(conf)); 500307164Ssephe conf.nvs_type = HN_NVS_TYPE_NDIS_CONF; 501307164Ssephe conf.nvs_mtu = mtu; 502307164Ssephe conf.nvs_caps = HN_NVS_NDIS_CONF_VLAN; 503314123Sdexuan if (sc->hn_nvs_ver >= HN_NVS_VERSION_5) 504314123Sdexuan conf.nvs_caps |= HN_NVS_NDIS_CONF_SRIOV; 505250199Sgrehan 506307167Ssephe /* NOTE: No response. */ 507307174Ssephe error = hn_nvs_req_send(sc, &conf, sizeof(conf)); 508307245Ssephe if (error) { 509307164Ssephe if_printf(sc->hn_ifp, "send nvs ndis conf failed: %d\n", error); 510307245Ssephe return (error); 511307245Ssephe } 512307245Ssephe 513307245Ssephe if (bootverbose) 514307245Ssephe if_printf(sc->hn_ifp, "nvs ndis conf done\n"); 515307245Ssephe sc->hn_caps |= HN_CAP_MTU | HN_CAP_VLAN; 516307245Ssephe return (0); 517250199Sgrehan} 518250199Sgrehan 519250199Sgrehanstatic int 520307202Ssephehn_nvs_init_ndis(struct hn_softc *sc) 521307202Ssephe{ 522307202Ssephe struct hn_nvs_ndis_init ndis; 523307202Ssephe int error; 524307202Ssephe 525307202Ssephe memset(&ndis, 0, sizeof(ndis)); 526307202Ssephe ndis.nvs_type = HN_NVS_TYPE_NDIS_INIT; 527307202Ssephe ndis.nvs_ndis_major = HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver); 528307202Ssephe ndis.nvs_ndis_minor = HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver); 529307202Ssephe 530307202Ssephe /* NOTE: No response. */ 531307202Ssephe error = hn_nvs_req_send(sc, &ndis, sizeof(ndis)); 532307202Ssephe if (error) 533307202Ssephe if_printf(sc->hn_ifp, "send nvs ndis init failed: %d\n", error); 534307202Ssephe return (error); 535307202Ssephe} 536307202Ssephe 537307202Ssephestatic int 538307200Ssephehn_nvs_init(struct hn_softc *sc) 539250199Sgrehan{ 540307246Ssephe int i, error; 541250199Sgrehan 542307245Ssephe if (device_is_attached(sc->hn_dev)) { 543307245Ssephe /* 544307245Ssephe * NVS version and NDIS version MUST NOT be changed. 545307245Ssephe */ 546307245Ssephe if (bootverbose) { 547307245Ssephe if_printf(sc->hn_ifp, "reinit NVS version 0x%x, " 548307245Ssephe "NDIS version %u.%u\n", sc->hn_nvs_ver, 549307245Ssephe HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver), 550307245Ssephe HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver)); 551307245Ssephe } 552307246Ssephe 553307246Ssephe error = hn_nvs_doinit(sc, sc->hn_nvs_ver); 554307246Ssephe if (error) { 555307246Ssephe if_printf(sc->hn_ifp, "reinit NVS version 0x%x " 556307246Ssephe "failed: %d\n", sc->hn_nvs_ver, error); 557308494Ssephe return (error); 558307246Ssephe } 559308494Ssephe goto done; 560307245Ssephe } 561307245Ssephe 562307245Ssephe /* 563307245Ssephe * Find the supported NVS version and set NDIS version accordingly. 564307245Ssephe */ 565307200Ssephe for (i = 0; i < nitems(hn_nvs_version); ++i) { 566307200Ssephe error = hn_nvs_doinit(sc, hn_nvs_version[i]); 567307200Ssephe if (!error) { 568307200Ssephe sc->hn_nvs_ver = hn_nvs_version[i]; 569307200Ssephe 570307200Ssephe /* Set NDIS version according to NVS version. */ 571307199Ssephe sc->hn_ndis_ver = HN_NDIS_VERSION_6_30; 572307200Ssephe if (sc->hn_nvs_ver <= HN_NVS_VERSION_4) 573307199Ssephe sc->hn_ndis_ver = HN_NDIS_VERSION_6_1; 574307200Ssephe 575307174Ssephe if (bootverbose) { 576307181Ssephe if_printf(sc->hn_ifp, "NVS version 0x%x, " 577307200Ssephe "NDIS version %u.%u\n", sc->hn_nvs_ver, 578307199Ssephe HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver), 579307199Ssephe HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver)); 580307174Ssephe } 581308494Ssephe goto done; 582250199Sgrehan } 583250199Sgrehan } 584307200Ssephe if_printf(sc->hn_ifp, "no NVS available\n"); 585307200Ssephe return (ENXIO); 586308494Ssephe 587308494Ssephedone: 588308494Ssephe if (sc->hn_nvs_ver >= HN_NVS_VERSION_5) 589308494Ssephe sc->hn_caps |= HN_CAP_HASHVAL; 590308494Ssephe return (0); 591307200Ssephe} 592250199Sgrehan 593307206Ssepheint 594307206Ssephehn_nvs_attach(struct hn_softc *sc, int mtu) 595307200Ssephe{ 596307206Ssephe int error; 597285236Swhu 598307202Ssephe /* 599307202Ssephe * Initialize NVS. 600307202Ssephe */ 601307206Ssephe error = hn_nvs_init(sc); 602307206Ssephe if (error) 603307206Ssephe return (error); 604307200Ssephe 605307202Ssephe if (sc->hn_nvs_ver >= HN_NVS_VERSION_2) { 606307202Ssephe /* 607307202Ssephe * Configure NDIS before initializing it. 608307202Ssephe */ 609307206Ssephe error = hn_nvs_conf_ndis(sc, mtu); 610307206Ssephe if (error) 611307206Ssephe return (error); 612307202Ssephe } 613250199Sgrehan 614250199Sgrehan /* 615307164Ssephe * Initialize NDIS. 616250199Sgrehan */ 617307206Ssephe error = hn_nvs_init_ndis(sc); 618307206Ssephe if (error) 619307206Ssephe return (error); 620250199Sgrehan 621307206Ssephe /* 622307206Ssephe * Connect RXBUF. 623307206Ssephe */ 624307206Ssephe error = hn_nvs_conn_rxbuf(sc); 625307206Ssephe if (error) 626307206Ssephe return (error); 627307206Ssephe 628307206Ssephe /* 629307206Ssephe * Connect chimney sending buffer. 630307206Ssephe */ 631307206Ssephe error = hn_nvs_conn_chim(sc); 632310757Ssephe if (error) { 633310757Ssephe hn_nvs_disconn_rxbuf(sc); 634307206Ssephe return (error); 635310757Ssephe } 636307206Ssephe return (0); 637250199Sgrehan} 638250199Sgrehan 639307250Ssephevoid 640307250Ssephehn_nvs_detach(struct hn_softc *sc) 641250199Sgrehan{ 642307250Ssephe 643307250Ssephe /* NOTE: there are no requests to stop the NVS. */ 644307206Ssephe hn_nvs_disconn_rxbuf(sc); 645307206Ssephe hn_nvs_disconn_chim(sc); 646250199Sgrehan} 647250199Sgrehan 648307185Ssephevoid 649308504Ssephehn_nvs_sent_xact(struct hn_nvs_sendctx *sndc, 650307174Ssephe struct hn_softc *sc __unused, struct vmbus_channel *chan __unused, 651307167Ssephe const void *data, int dlen) 652307161Ssephe{ 653307164Ssephe 654307167Ssephe vmbus_xact_wakeup(sndc->hn_cbarg, data, dlen); 655307161Ssephe} 656307161Ssephe 657307161Ssephestatic void 658308504Ssephehn_nvs_sent_none(struct hn_nvs_sendctx *sndc __unused, 659307174Ssephe struct hn_softc *sc __unused, struct vmbus_channel *chan __unused, 660307167Ssephe const void *data __unused, int dlen __unused) 661307161Ssephe{ 662307161Ssephe /* EMPTY */ 663307161Ssephe} 664307161Ssephe 665250199Sgrehanint 666307206Ssephehn_nvs_alloc_subchans(struct hn_softc *sc, int *nsubch0) 667307206Ssephe{ 668307206Ssephe struct vmbus_xact *xact; 669307206Ssephe struct hn_nvs_subch_req *req; 670307206Ssephe const struct hn_nvs_subch_resp *resp; 671307206Ssephe int error, nsubch_req; 672307206Ssephe uint32_t nsubch; 673307206Ssephe size_t resp_len; 674307206Ssephe 675307206Ssephe nsubch_req = *nsubch0; 676307206Ssephe KASSERT(nsubch_req > 0, ("invalid # of sub-channels %d", nsubch_req)); 677307206Ssephe 678307206Ssephe xact = vmbus_xact_get(sc->hn_xact, sizeof(*req)); 679307206Ssephe if (xact == NULL) { 680307206Ssephe if_printf(sc->hn_ifp, "no xact for nvs subch alloc\n"); 681307206Ssephe return (ENXIO); 682307206Ssephe } 683307206Ssephe req = vmbus_xact_req_data(xact); 684307206Ssephe req->nvs_type = HN_NVS_TYPE_SUBCH_REQ; 685307206Ssephe req->nvs_op = HN_NVS_SUBCH_OP_ALLOC; 686307206Ssephe req->nvs_nsubch = nsubch_req; 687307206Ssephe 688307206Ssephe resp_len = sizeof(*resp); 689307206Ssephe resp = hn_nvs_xact_execute(sc, xact, req, sizeof(*req), &resp_len, 690307206Ssephe HN_NVS_TYPE_SUBCH_RESP); 691307206Ssephe if (resp == NULL) { 692307206Ssephe if_printf(sc->hn_ifp, "exec nvs subch alloc failed\n"); 693307206Ssephe error = EIO; 694307206Ssephe goto done; 695307206Ssephe } 696307206Ssephe if (resp->nvs_status != HN_NVS_STATUS_OK) { 697307206Ssephe if_printf(sc->hn_ifp, "nvs subch alloc failed: %x\n", 698307206Ssephe resp->nvs_status); 699307206Ssephe error = EIO; 700307206Ssephe goto done; 701307206Ssephe } 702307206Ssephe 703307206Ssephe nsubch = resp->nvs_nsubch; 704307206Ssephe if (nsubch > nsubch_req) { 705307206Ssephe if_printf(sc->hn_ifp, "%u subchans are allocated, " 706307206Ssephe "requested %d\n", nsubch, nsubch_req); 707307206Ssephe nsubch = nsubch_req; 708307206Ssephe } 709307206Ssephe *nsubch0 = nsubch; 710307206Ssephe error = 0; 711307206Ssephedone: 712307206Ssephe vmbus_xact_put(xact); 713307206Ssephe return (error); 714307206Ssephe} 715308504Ssephe 716308504Ssepheint 717308504Ssephehn_nvs_send_rndis_ctrl(struct vmbus_channel *chan, 718308504Ssephe struct hn_nvs_sendctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt) 719308504Ssephe{ 720308504Ssephe 721308504Ssephe return hn_nvs_send_rndis_sglist(chan, HN_NVS_RNDIS_MTYPE_CTRL, 722308504Ssephe sndc, gpa, gpa_cnt); 723308504Ssephe} 724314123Sdexuan 725314123Sdexuanvoid 726314123Sdexuanhn_nvs_set_datapath(struct hn_softc *sc, uint32_t path) 727314123Sdexuan{ 728314123Sdexuan struct hn_nvs_datapath dp; 729314123Sdexuan 730314123Sdexuan memset(&dp, 0, sizeof(dp)); 731314123Sdexuan dp.nvs_type = HN_NVS_TYPE_SET_DATAPATH; 732314123Sdexuan dp.nvs_active_path = path; 733314123Sdexuan 734314123Sdexuan hn_nvs_req_send(sc, &dp, sizeof(dp)); 735314123Sdexuan} 736