1 /*- 2 * Copyright (c) 2009-2012,2016-2017 Microsoft Corp. 3 * Copyright (c) 2010-2012 Citrix Inc. 4 * Copyright (c) 2012 NetApp Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice unmodified, this list of conditions, and the following 12 * disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 #include "opt_inet6.h" 31 #include "opt_inet.h" 32 33 #include <sys/param.h> 34 #include <sys/socket.h> 35 #include <sys/systm.h> 36 #include <sys/taskqueue.h> 37 38 #include <machine/atomic.h> 39 40 #include <net/ethernet.h> 41 #include <net/if.h> 42 #include <net/if_var.h> 43 #include <net/if_media.h> 44 #include <net/rndis.h> 45 46 #include <netinet/in.h> 47 #include <netinet/ip.h> 48 #include <netinet/tcp_lro.h> 49 50 #include <dev/hyperv/include/hyperv.h> 51 #include <dev/hyperv/include/vmbus.h> 52 #include <dev/hyperv/include/vmbus_xact.h> 53 54 #include <dev/hyperv/netvsc/ndis.h> 55 #include <dev/hyperv/netvsc/if_hnreg.h> 56 #include <dev/hyperv/netvsc/if_hnvar.h> 57 #include <dev/hyperv/netvsc/hn_nvs.h> 58 #include <dev/hyperv/netvsc/hn_rndis.h> 59 60 #define HN_RNDIS_RID_COMPAT_MASK 0xffff 61 #define HN_RNDIS_RID_COMPAT_MAX HN_RNDIS_RID_COMPAT_MASK 62 63 #define HN_RNDIS_XFER_SIZE 2048 64 65 #define HN_NDIS_TXCSUM_CAP_IP4 \ 66 (NDIS_TXCSUM_CAP_IP4 | NDIS_TXCSUM_CAP_IP4OPT) 67 #define HN_NDIS_TXCSUM_CAP_TCP4 \ 68 (NDIS_TXCSUM_CAP_TCP4 | NDIS_TXCSUM_CAP_TCP4OPT) 69 #define HN_NDIS_TXCSUM_CAP_TCP6 \ 70 (NDIS_TXCSUM_CAP_TCP6 | NDIS_TXCSUM_CAP_TCP6OPT | \ 71 NDIS_TXCSUM_CAP_IP6EXT) 72 #define HN_NDIS_TXCSUM_CAP_UDP6 \ 73 (NDIS_TXCSUM_CAP_UDP6 | NDIS_TXCSUM_CAP_IP6EXT) 74 #define HN_NDIS_LSOV2_CAP_IP6 \ 75 (NDIS_LSOV2_CAP_IP6EXT | NDIS_LSOV2_CAP_TCP6OPT) 76 77 static const void *hn_rndis_xact_exec1(struct hn_softc *, 78 struct vmbus_xact *, size_t, 79 struct hn_nvs_sendctx *, size_t *); 80 static const void *hn_rndis_xact_execute(struct hn_softc *, 81 struct vmbus_xact *, uint32_t, size_t, size_t *, 82 uint32_t); 83 static int hn_rndis_query(struct hn_softc *, uint32_t, 84 const void *, size_t, void *, size_t *); 85 static int hn_rndis_query2(struct hn_softc *, uint32_t, 86 const void *, size_t, void *, size_t *, size_t); 87 static int hn_rndis_set(struct hn_softc *, uint32_t, 88 const void *, size_t); 89 static int hn_rndis_init(struct hn_softc *); 90 static int hn_rndis_halt(struct hn_softc *); 91 static int hn_rndis_conf_offload(struct hn_softc *, int); 92 static int hn_rndis_query_hwcaps(struct hn_softc *, 93 struct ndis_offload *); 94 95 static __inline uint32_t 96 hn_rndis_rid(struct hn_softc *sc) 97 { 98 uint32_t rid; 99 100 again: 101 rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1); 102 if (rid == 0) 103 goto again; 104 105 /* Use upper 16 bits for non-compat RNDIS messages. */ 106 return ((rid & 0xffff) << 16); 107 } 108 109 void 110 hn_rndis_rx_ctrl(struct hn_softc *sc, const void *data, int dlen) 111 { 112 const struct rndis_comp_hdr *comp; 113 const struct rndis_msghdr *hdr; 114 115 KASSERT(dlen >= sizeof(*hdr), ("invalid RNDIS msg\n")); 116 hdr = data; 117 118 switch (hdr->rm_type) { 119 case REMOTE_NDIS_INITIALIZE_CMPLT: 120 case REMOTE_NDIS_QUERY_CMPLT: 121 case REMOTE_NDIS_SET_CMPLT: 122 case REMOTE_NDIS_KEEPALIVE_CMPLT: /* unused */ 123 if (dlen < sizeof(*comp)) { 124 if_printf(sc->hn_ifp, "invalid RNDIS cmplt\n"); 125 return; 126 } 127 comp = data; 128 129 KASSERT(comp->rm_rid > HN_RNDIS_RID_COMPAT_MAX, 130 ("invalid RNDIS rid 0x%08x\n", comp->rm_rid)); 131 vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen); 132 break; 133 134 case REMOTE_NDIS_RESET_CMPLT: 135 /* 136 * Reset completed, no rid. 137 * 138 * NOTE: 139 * RESET is not issued by hn(4), so this message should 140 * _not_ be observed. 141 */ 142 if_printf(sc->hn_ifp, "RESET cmplt received\n"); 143 break; 144 145 default: 146 if_printf(sc->hn_ifp, "unknown RNDIS msg 0x%x\n", 147 hdr->rm_type); 148 break; 149 } 150 } 151 152 int 153 hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr) 154 { 155 size_t eaddr_len; 156 int error; 157 158 eaddr_len = ETHER_ADDR_LEN; 159 error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0, 160 eaddr, &eaddr_len); 161 if (error) 162 return (error); 163 if (eaddr_len != ETHER_ADDR_LEN) { 164 if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len); 165 return (EINVAL); 166 } 167 return (0); 168 } 169 170 int 171 hn_rndis_get_linkstatus(struct hn_softc *sc, uint32_t *link_status) 172 { 173 size_t size; 174 int error; 175 176 size = sizeof(*link_status); 177 error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0, 178 link_status, &size); 179 if (error) 180 return (error); 181 if (size != sizeof(uint32_t)) { 182 if_printf(sc->hn_ifp, "invalid link status len %zu\n", size); 183 return (EINVAL); 184 } 185 return (0); 186 } 187 188 int 189 hn_rndis_get_mtu(struct hn_softc *sc, uint32_t *mtu) 190 { 191 size_t size; 192 int error; 193 194 size = sizeof(*mtu); 195 error = hn_rndis_query(sc, OID_GEN_MAXIMUM_FRAME_SIZE, NULL, 0, 196 mtu, &size); 197 if (error) 198 return (error); 199 if (size != sizeof(uint32_t)) { 200 if_printf(sc->hn_ifp, "invalid mtu len %zu\n", size); 201 return (EINVAL); 202 } 203 return (0); 204 } 205 206 static const void * 207 hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen, 208 struct hn_nvs_sendctx *sndc, size_t *comp_len) 209 { 210 struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT]; 211 int gpa_cnt, error; 212 bus_addr_t paddr; 213 214 KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0, 215 ("invalid request length %zu", reqlen)); 216 217 /* 218 * Setup the SG list. 219 */ 220 paddr = vmbus_xact_req_paddr(xact); 221 KASSERT((paddr & PAGE_MASK) == 0, 222 ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr)); 223 for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) { 224 int len = PAGE_SIZE; 225 226 if (reqlen == 0) 227 break; 228 if (reqlen < len) 229 len = reqlen; 230 231 gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt; 232 gpa[gpa_cnt].gpa_len = len; 233 gpa[gpa_cnt].gpa_ofs = 0; 234 235 reqlen -= len; 236 } 237 KASSERT(reqlen == 0, ("still have %zu request data left", reqlen)); 238 239 /* 240 * Send this RNDIS control message and wait for its completion 241 * message. 242 */ 243 vmbus_xact_activate(xact); 244 error = hn_nvs_send_rndis_ctrl(sc->hn_prichan, sndc, gpa, gpa_cnt); 245 if (error) { 246 vmbus_xact_deactivate(xact); 247 if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error); 248 return (NULL); 249 } 250 return (vmbus_chan_xact_wait(sc->hn_prichan, xact, comp_len, 251 HN_CAN_SLEEP(sc))); 252 } 253 254 static const void * 255 hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid, 256 size_t reqlen, size_t *comp_len0, uint32_t comp_type) 257 { 258 const struct rndis_comp_hdr *comp; 259 size_t comp_len, min_complen = *comp_len0; 260 261 KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid)); 262 KASSERT(min_complen >= sizeof(*comp), 263 ("invalid minimum complete len %zu", min_complen)); 264 265 /* 266 * Execute the xact setup by the caller. 267 */ 268 comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_nvs_sendctx_none, 269 &comp_len); 270 if (comp == NULL) 271 return (NULL); 272 273 /* 274 * Check this RNDIS complete message. 275 */ 276 if (comp_len < min_complen) { 277 if (comp_len >= sizeof(*comp)) { 278 /* rm_status field is valid */ 279 if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, " 280 "status 0x%08x\n", comp_len, comp->rm_status); 281 } else { 282 if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n", 283 comp_len); 284 } 285 return (NULL); 286 } 287 if (comp->rm_len < min_complen) { 288 if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n", 289 comp->rm_len); 290 return (NULL); 291 } 292 if (comp->rm_type != comp_type) { 293 if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, " 294 "expect 0x%08x\n", comp->rm_type, comp_type); 295 return (NULL); 296 } 297 if (comp->rm_rid != rid) { 298 if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, " 299 "expect %u\n", comp->rm_rid, rid); 300 return (NULL); 301 } 302 /* All pass! */ 303 *comp_len0 = comp_len; 304 return (comp); 305 } 306 307 static int 308 hn_rndis_query(struct hn_softc *sc, uint32_t oid, 309 const void *idata, size_t idlen, void *odata, size_t *odlen0) 310 { 311 312 return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0)); 313 } 314 315 static int 316 hn_rndis_query2(struct hn_softc *sc, uint32_t oid, 317 const void *idata, size_t idlen, void *odata, size_t *odlen0, 318 size_t min_odlen) 319 { 320 struct rndis_query_req *req; 321 const struct rndis_query_comp *comp; 322 struct vmbus_xact *xact; 323 size_t reqlen, odlen = *odlen0, comp_len; 324 int error, ofs; 325 uint32_t rid; 326 327 reqlen = sizeof(*req) + idlen; 328 xact = vmbus_xact_get(sc->hn_xact, reqlen); 329 if (xact == NULL) { 330 if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid); 331 return (ENXIO); 332 } 333 rid = hn_rndis_rid(sc); 334 req = vmbus_xact_req_data(xact); 335 req->rm_type = REMOTE_NDIS_QUERY_MSG; 336 req->rm_len = reqlen; 337 req->rm_rid = rid; 338 req->rm_oid = oid; 339 /* 340 * XXX 341 * This is _not_ RNDIS Spec conforming: 342 * "This MUST be set to 0 when there is no input data 343 * associated with the OID." 344 * 345 * If this field was set to 0 according to the RNDIS Spec, 346 * Hyper-V would set non-SUCCESS status in the query 347 * completion. 348 */ 349 req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET; 350 351 if (idlen > 0) { 352 req->rm_infobuflen = idlen; 353 /* Input data immediately follows RNDIS query. */ 354 memcpy(req + 1, idata, idlen); 355 } 356 357 comp_len = sizeof(*comp) + min_odlen; 358 comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len, 359 REMOTE_NDIS_QUERY_CMPLT); 360 if (comp == NULL) { 361 if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid); 362 error = EIO; 363 goto done; 364 } 365 366 if (comp->rm_status != RNDIS_STATUS_SUCCESS) { 367 if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: " 368 "status 0x%08x\n", oid, comp->rm_status); 369 error = EIO; 370 goto done; 371 } 372 if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) { 373 /* No output data! */ 374 if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid); 375 *odlen0 = 0; 376 error = 0; 377 goto done; 378 } 379 380 /* 381 * Check output data length and offset. 382 */ 383 /* ofs is the offset from the beginning of comp. */ 384 ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset); 385 if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) { 386 if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, " 387 "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen); 388 error = EINVAL; 389 goto done; 390 } 391 392 /* 393 * Save output data. 394 */ 395 if (comp->rm_infobuflen < odlen) 396 odlen = comp->rm_infobuflen; 397 memcpy(odata, ((const uint8_t *)comp) + ofs, odlen); 398 *odlen0 = odlen; 399 400 error = 0; 401 done: 402 vmbus_xact_put(xact); 403 return (error); 404 } 405 406 int 407 hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0) 408 { 409 struct ndis_rss_caps in, caps; 410 size_t caps_len; 411 int error, indsz, rxr_cnt, hash_fnidx; 412 uint32_t hash_func = 0, hash_types = 0; 413 414 *rxr_cnt0 = 0; 415 416 if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20) 417 return (EOPNOTSUPP); 418 419 memset(&in, 0, sizeof(in)); 420 in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS; 421 in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2; 422 in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE; 423 424 caps_len = NDIS_RSS_CAPS_SIZE; 425 error = hn_rndis_query2(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES, 426 &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len, NDIS_RSS_CAPS_SIZE_6_0); 427 if (error) 428 return (error); 429 430 /* 431 * Preliminary verification. 432 */ 433 if (caps.ndis_hdr.ndis_type != NDIS_OBJTYPE_RSS_CAPS) { 434 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n", 435 caps.ndis_hdr.ndis_type); 436 return (EINVAL); 437 } 438 if (caps.ndis_hdr.ndis_rev < NDIS_RSS_CAPS_REV_1) { 439 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n", 440 caps.ndis_hdr.ndis_rev); 441 return (EINVAL); 442 } 443 if (caps.ndis_hdr.ndis_size > caps_len) { 444 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, " 445 "data size %zu\n", caps.ndis_hdr.ndis_size, caps_len); 446 return (EINVAL); 447 } else if (caps.ndis_hdr.ndis_size < NDIS_RSS_CAPS_SIZE_6_0) { 448 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n", 449 caps.ndis_hdr.ndis_size); 450 return (EINVAL); 451 } 452 453 /* 454 * Save information for later RSS configuration. 455 */ 456 if (caps.ndis_nrxr == 0) { 457 if_printf(sc->hn_ifp, "0 RX rings!?\n"); 458 return (EINVAL); 459 } 460 if (bootverbose) 461 if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr); 462 rxr_cnt = caps.ndis_nrxr; 463 464 if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE && 465 caps.ndis_hdr.ndis_rev >= NDIS_RSS_CAPS_REV_2) { 466 if (caps.ndis_nind > NDIS_HASH_INDCNT) { 467 if_printf(sc->hn_ifp, 468 "too many RSS indirect table entries %u\n", 469 caps.ndis_nind); 470 return (EOPNOTSUPP); 471 } 472 if (!powerof2(caps.ndis_nind)) { 473 if_printf(sc->hn_ifp, "RSS indirect table size is not " 474 "power-of-2 %u\n", caps.ndis_nind); 475 } 476 477 if (bootverbose) { 478 if_printf(sc->hn_ifp, "RSS indirect table size %u\n", 479 caps.ndis_nind); 480 } 481 indsz = caps.ndis_nind; 482 } else { 483 indsz = NDIS_HASH_INDCNT; 484 } 485 if (indsz < rxr_cnt) { 486 if_printf(sc->hn_ifp, "# of RX rings (%d) > " 487 "RSS indirect table size %d\n", rxr_cnt, indsz); 488 rxr_cnt = indsz; 489 } 490 491 /* 492 * NOTE: 493 * Toeplitz is at the lowest bit, and it is preferred; so ffs(), 494 * instead of fls(), is used here. 495 */ 496 hash_fnidx = ffs(caps.ndis_caps & NDIS_RSS_CAP_HASHFUNC_MASK); 497 if (hash_fnidx == 0) { 498 if_printf(sc->hn_ifp, "no hash functions, caps 0x%08x\n", 499 caps.ndis_caps); 500 return (EOPNOTSUPP); 501 } 502 hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */ 503 504 if (caps.ndis_caps & NDIS_RSS_CAP_IPV4) 505 hash_types |= NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4; 506 if (caps.ndis_caps & NDIS_RSS_CAP_IPV6) 507 hash_types |= NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6; 508 if (caps.ndis_caps & NDIS_RSS_CAP_IPV6_EX) 509 hash_types |= NDIS_HASH_IPV6_EX | NDIS_HASH_TCP_IPV6_EX; 510 if (hash_types == 0) { 511 if_printf(sc->hn_ifp, "no hash types, caps 0x%08x\n", 512 caps.ndis_caps); 513 return (EOPNOTSUPP); 514 } 515 if (bootverbose) 516 if_printf(sc->hn_ifp, "RSS caps %#x\n", caps.ndis_caps); 517 518 /* Commit! */ 519 sc->hn_rss_ind_size = indsz; 520 sc->hn_rss_hcap = hash_func | hash_types; 521 if (sc->hn_caps & HN_CAP_UDPHASH) { 522 /* UDP 4-tuple hash is unconditionally enabled. */ 523 sc->hn_rss_hcap |= NDIS_HASH_UDP_IPV4_X; 524 } 525 *rxr_cnt0 = rxr_cnt; 526 return (0); 527 } 528 529 static int 530 hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen) 531 { 532 struct rndis_set_req *req; 533 const struct rndis_set_comp *comp; 534 struct vmbus_xact *xact; 535 size_t reqlen, comp_len; 536 uint32_t rid; 537 int error; 538 539 KASSERT(dlen > 0, ("invalid dlen %zu", dlen)); 540 541 reqlen = sizeof(*req) + dlen; 542 xact = vmbus_xact_get(sc->hn_xact, reqlen); 543 if (xact == NULL) { 544 if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid); 545 return (ENXIO); 546 } 547 rid = hn_rndis_rid(sc); 548 req = vmbus_xact_req_data(xact); 549 req->rm_type = REMOTE_NDIS_SET_MSG; 550 req->rm_len = reqlen; 551 req->rm_rid = rid; 552 req->rm_oid = oid; 553 req->rm_infobuflen = dlen; 554 req->rm_infobufoffset = RNDIS_SET_REQ_INFOBUFOFFSET; 555 /* Data immediately follows RNDIS set. */ 556 memcpy(req + 1, data, dlen); 557 558 comp_len = sizeof(*comp); 559 comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len, 560 REMOTE_NDIS_SET_CMPLT); 561 if (comp == NULL) { 562 if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid); 563 error = EIO; 564 goto done; 565 } 566 567 if (comp->rm_status != RNDIS_STATUS_SUCCESS) { 568 if_printf(sc->hn_ifp, "RNDIS set 0x%08x failed: " 569 "status 0x%08x\n", oid, comp->rm_status); 570 error = EIO; 571 goto done; 572 } 573 error = 0; 574 done: 575 vmbus_xact_put(xact); 576 return (error); 577 } 578 579 int 580 hn_rndis_reconf_offload(struct hn_softc *sc, int mtu) 581 { 582 return(hn_rndis_conf_offload(sc, mtu)); 583 } 584 585 static int 586 hn_rndis_conf_offload(struct hn_softc *sc, int mtu) 587 { 588 struct ndis_offload hwcaps; 589 struct ndis_offload_params params; 590 uint32_t caps = 0; 591 size_t paramsz; 592 int error, tso_maxsz, tso_minsg; 593 594 error = hn_rndis_query_hwcaps(sc, &hwcaps); 595 if (error) { 596 if_printf(sc->hn_ifp, "hwcaps query failed: %d\n", error); 597 return (error); 598 } 599 600 /* NOTE: 0 means "no change" */ 601 memset(¶ms, 0, sizeof(params)); 602 603 params.ndis_hdr.ndis_type = NDIS_OBJTYPE_DEFAULT; 604 if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30) { 605 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_2; 606 paramsz = NDIS_OFFLOAD_PARAMS_SIZE_6_1; 607 } else { 608 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3; 609 paramsz = NDIS_OFFLOAD_PARAMS_SIZE; 610 } 611 params.ndis_hdr.ndis_size = paramsz; 612 613 /* 614 * TSO4/TSO6 setup. 615 */ 616 tso_maxsz = IP_MAXPACKET; 617 tso_minsg = 2; 618 if (hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) { 619 caps |= HN_CAP_TSO4; 620 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON; 621 622 if (hwcaps.ndis_lsov2.ndis_ip4_maxsz < tso_maxsz) 623 tso_maxsz = hwcaps.ndis_lsov2.ndis_ip4_maxsz; 624 if (hwcaps.ndis_lsov2.ndis_ip4_minsg > tso_minsg) 625 tso_minsg = hwcaps.ndis_lsov2.ndis_ip4_minsg; 626 } 627 if ((hwcaps.ndis_lsov2.ndis_ip6_encap & NDIS_OFFLOAD_ENCAP_8023) && 628 (hwcaps.ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6) == 629 HN_NDIS_LSOV2_CAP_IP6) { 630 caps |= HN_CAP_TSO6; 631 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON; 632 633 if (hwcaps.ndis_lsov2.ndis_ip6_maxsz < tso_maxsz) 634 tso_maxsz = hwcaps.ndis_lsov2.ndis_ip6_maxsz; 635 if (hwcaps.ndis_lsov2.ndis_ip6_minsg > tso_minsg) 636 tso_minsg = hwcaps.ndis_lsov2.ndis_ip6_minsg; 637 } 638 sc->hn_ndis_tso_szmax = 0; 639 sc->hn_ndis_tso_sgmin = 0; 640 if (caps & (HN_CAP_TSO4 | HN_CAP_TSO6)) { 641 KASSERT(tso_maxsz <= IP_MAXPACKET, 642 ("invalid NDIS TSO maxsz %d", tso_maxsz)); 643 KASSERT(tso_minsg >= 2, 644 ("invalid NDIS TSO minsg %d", tso_minsg)); 645 if (tso_maxsz < tso_minsg * mtu) { 646 if_printf(sc->hn_ifp, "invalid NDIS TSO config: " 647 "maxsz %d, minsg %d, mtu %d; " 648 "disable TSO4 and TSO6\n", 649 tso_maxsz, tso_minsg, mtu); 650 caps &= ~(HN_CAP_TSO4 | HN_CAP_TSO6); 651 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_OFF; 652 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_OFF; 653 } else { 654 sc->hn_ndis_tso_szmax = tso_maxsz; 655 sc->hn_ndis_tso_sgmin = tso_minsg; 656 if (bootverbose) { 657 if_printf(sc->hn_ifp, "NDIS TSO " 658 "szmax %d sgmin %d\n", 659 sc->hn_ndis_tso_szmax, 660 sc->hn_ndis_tso_sgmin); 661 } 662 } 663 } 664 665 /* IPv4 checksum */ 666 if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4) == 667 HN_NDIS_TXCSUM_CAP_IP4) { 668 caps |= HN_CAP_IPCS; 669 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TX; 670 } 671 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4) { 672 if (params.ndis_ip4csum == NDIS_OFFLOAD_PARAM_TX) 673 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX; 674 else 675 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_RX; 676 } 677 678 /* TCP4 checksum */ 679 if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_TCP4) == 680 HN_NDIS_TXCSUM_CAP_TCP4) { 681 caps |= HN_CAP_TCP4CS; 682 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TX; 683 } 684 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) { 685 if (params.ndis_tcp4csum == NDIS_OFFLOAD_PARAM_TX) 686 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX; 687 else 688 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_RX; 689 } 690 691 /* UDP4 checksum */ 692 if (hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) { 693 caps |= HN_CAP_UDP4CS; 694 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TX; 695 } 696 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) { 697 if (params.ndis_udp4csum == NDIS_OFFLOAD_PARAM_TX) 698 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX; 699 else 700 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_RX; 701 } 702 703 /* TCP6 checksum */ 704 if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_TCP6) == 705 HN_NDIS_TXCSUM_CAP_TCP6) { 706 caps |= HN_CAP_TCP6CS; 707 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TX; 708 } 709 if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6) { 710 if (params.ndis_tcp6csum == NDIS_OFFLOAD_PARAM_TX) 711 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX; 712 else 713 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_RX; 714 } 715 716 /* UDP6 checksum */ 717 if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_UDP6) == 718 HN_NDIS_TXCSUM_CAP_UDP6) { 719 caps |= HN_CAP_UDP6CS; 720 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TX; 721 } 722 if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6) { 723 if (params.ndis_udp6csum == NDIS_OFFLOAD_PARAM_TX) 724 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX; 725 else 726 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_RX; 727 } 728 729 /* RSC offload */ 730 if (hwcaps.ndis_hdr.ndis_rev >= NDIS_OFFLOAD_PARAMS_REV_3) { 731 if (hwcaps.ndis_rsc.ndis_ip4 && hwcaps.ndis_rsc.ndis_ip6 && 732 sc->hn_rsc_ctrl) { 733 params.ndis_rsc_ip4 = NDIS_OFFLOAD_RSC_ON; 734 params.ndis_rsc_ip6 = NDIS_OFFLOAD_RSC_ON; 735 } else { 736 params.ndis_rsc_ip4 = NDIS_OFFLOAD_RSC_OFF; 737 params.ndis_rsc_ip6 = NDIS_OFFLOAD_RSC_OFF; 738 } 739 } 740 741 if (bootverbose) { 742 if_printf(sc->hn_ifp, "offload csum: " 743 "ip4 %u, tcp4 %u, udp4 %u, tcp6 %u, udp6 %u\n", 744 params.ndis_ip4csum, 745 params.ndis_tcp4csum, 746 params.ndis_udp4csum, 747 params.ndis_tcp6csum, 748 params.ndis_udp6csum); 749 if_printf(sc->hn_ifp, "offload lsov2: ip4 %u, ip6 %u\n", 750 params.ndis_lsov2_ip4, 751 params.ndis_lsov2_ip6); 752 if (hwcaps.ndis_hdr.ndis_rev >= NDIS_OFFLOAD_PARAMS_REV_3) 753 if_printf(sc->hn_ifp, "offload rsc: ip4 %u, ip6 %u\n", 754 params.ndis_rsc_ip4, 755 params.ndis_rsc_ip6); 756 } 757 758 error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, ¶ms, paramsz); 759 if (error) { 760 if_printf(sc->hn_ifp, "offload config failed: %d\n", error); 761 return (error); 762 } 763 764 if (bootverbose) 765 if_printf(sc->hn_ifp, "offload config done\n"); 766 sc->hn_caps |= caps; 767 return (0); 768 } 769 770 int 771 hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags) 772 { 773 struct ndis_rssprm_toeplitz *rss = &sc->hn_rss; 774 struct ndis_rss_params *prm = &rss->rss_params; 775 int error, rss_size; 776 777 /* 778 * Only NDIS 6.20+ is supported: 779 * We only support 4bytes element in indirect table, which has been 780 * adopted since NDIS 6.20. 781 */ 782 KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_20, 783 ("NDIS 6.20+ is required, NDIS version 0x%08x", sc->hn_ndis_ver)); 784 785 /* XXX only one can be specified through, popcnt? */ 786 KASSERT((sc->hn_rss_hash & NDIS_HASH_FUNCTION_MASK), 787 ("no hash func %08x", sc->hn_rss_hash)); 788 KASSERT((sc->hn_rss_hash & NDIS_HASH_STD), 789 ("no standard hash types %08x", sc->hn_rss_hash)); 790 KASSERT(sc->hn_rss_ind_size > 0, ("no indirect table size")); 791 792 if (bootverbose) { 793 if_printf(sc->hn_ifp, "RSS indirect table size %d, " 794 "hash 0x%08x\n", sc->hn_rss_ind_size, sc->hn_rss_hash); 795 } 796 797 /* 798 * NOTE: 799 * DO NOT whack rss_key and rss_ind, which are setup by the caller. 800 */ 801 memset(prm, 0, sizeof(*prm)); 802 rss_size = NDIS_RSSPRM_TOEPLITZ_SIZE(sc->hn_rss_ind_size); 803 804 prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS; 805 prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2; 806 prm->ndis_hdr.ndis_size = rss_size; 807 prm->ndis_flags = flags; 808 prm->ndis_hash = sc->hn_rss_hash & 809 (NDIS_HASH_FUNCTION_MASK | NDIS_HASH_STD); 810 prm->ndis_indsize = sizeof(rss->rss_ind[0]) * sc->hn_rss_ind_size; 811 prm->ndis_indoffset = 812 __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]); 813 prm->ndis_keysize = sizeof(rss->rss_key); 814 prm->ndis_keyoffset = 815 __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]); 816 817 error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS, 818 rss, rss_size); 819 if (error) { 820 if_printf(sc->hn_ifp, "RSS config failed: %d\n", error); 821 } else { 822 if (bootverbose) 823 if_printf(sc->hn_ifp, "RSS config done\n"); 824 } 825 return (error); 826 } 827 828 int 829 hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter) 830 { 831 int error; 832 833 error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER, 834 &filter, sizeof(filter)); 835 if (error) { 836 if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n", 837 filter, error); 838 } else { 839 if (bootverbose) { 840 if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n", 841 filter); 842 } 843 } 844 return (error); 845 } 846 847 static int 848 hn_rndis_init(struct hn_softc *sc) 849 { 850 struct rndis_init_req *req; 851 const struct rndis_init_comp *comp; 852 struct vmbus_xact *xact; 853 size_t comp_len; 854 uint32_t rid; 855 int error; 856 857 xact = vmbus_xact_get(sc->hn_xact, sizeof(*req)); 858 if (xact == NULL) { 859 if_printf(sc->hn_ifp, "no xact for RNDIS init\n"); 860 return (ENXIO); 861 } 862 rid = hn_rndis_rid(sc); 863 req = vmbus_xact_req_data(xact); 864 req->rm_type = REMOTE_NDIS_INITIALIZE_MSG; 865 req->rm_len = sizeof(*req); 866 req->rm_rid = rid; 867 req->rm_ver_major = RNDIS_VERSION_MAJOR; 868 req->rm_ver_minor = RNDIS_VERSION_MINOR; 869 req->rm_max_xfersz = HN_RNDIS_XFER_SIZE; 870 871 comp_len = RNDIS_INIT_COMP_SIZE_MIN; 872 comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len, 873 REMOTE_NDIS_INITIALIZE_CMPLT); 874 if (comp == NULL) { 875 if_printf(sc->hn_ifp, "exec RNDIS init failed\n"); 876 error = EIO; 877 goto done; 878 } 879 880 if (comp->rm_status != RNDIS_STATUS_SUCCESS) { 881 if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n", 882 comp->rm_status); 883 error = EIO; 884 goto done; 885 } 886 sc->hn_rndis_agg_size = comp->rm_pktmaxsz; 887 sc->hn_rndis_agg_pkts = comp->rm_pktmaxcnt; 888 sc->hn_rndis_agg_align = 1U << comp->rm_align; 889 890 if (sc->hn_rndis_agg_align < sizeof(uint32_t)) { 891 /* 892 * The RNDIS packet messsage encap assumes that the RNDIS 893 * packet message is at least 4 bytes aligned. Fix up the 894 * alignment here, if the remote side sets the alignment 895 * too low. 896 */ 897 if_printf(sc->hn_ifp, "fixup RNDIS aggpkt align: %u -> %zu\n", 898 sc->hn_rndis_agg_align, sizeof(uint32_t)); 899 sc->hn_rndis_agg_align = sizeof(uint32_t); 900 } 901 902 if (bootverbose) { 903 if_printf(sc->hn_ifp, "RNDIS ver %u.%u, " 904 "aggpkt size %u, aggpkt cnt %u, aggpkt align %u\n", 905 comp->rm_ver_major, comp->rm_ver_minor, 906 sc->hn_rndis_agg_size, sc->hn_rndis_agg_pkts, 907 sc->hn_rndis_agg_align); 908 } 909 error = 0; 910 done: 911 vmbus_xact_put(xact); 912 return (error); 913 } 914 915 static int 916 hn_rndis_halt(struct hn_softc *sc) 917 { 918 struct vmbus_xact *xact; 919 struct rndis_halt_req *halt; 920 struct hn_nvs_sendctx sndc; 921 size_t comp_len; 922 923 xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt)); 924 if (xact == NULL) { 925 if_printf(sc->hn_ifp, "no xact for RNDIS halt\n"); 926 return (ENXIO); 927 } 928 halt = vmbus_xact_req_data(xact); 929 halt->rm_type = REMOTE_NDIS_HALT_MSG; 930 halt->rm_len = sizeof(*halt); 931 halt->rm_rid = hn_rndis_rid(sc); 932 933 /* No RNDIS completion; rely on NVS message send completion */ 934 hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact); 935 hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len); 936 937 vmbus_xact_put(xact); 938 if (bootverbose) 939 if_printf(sc->hn_ifp, "RNDIS halt done\n"); 940 return (0); 941 } 942 943 static int 944 hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps) 945 { 946 struct ndis_offload in; 947 size_t caps_len, size; 948 int error; 949 950 memset(&in, 0, sizeof(in)); 951 in.ndis_hdr.ndis_type = NDIS_OBJTYPE_OFFLOAD; 952 if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) { 953 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_3; 954 size = NDIS_OFFLOAD_SIZE; 955 } else if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_1) { 956 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_2; 957 size = NDIS_OFFLOAD_SIZE_6_1; 958 } else { 959 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1; 960 size = NDIS_OFFLOAD_SIZE_6_0; 961 } 962 in.ndis_hdr.ndis_size = size; 963 964 caps_len = NDIS_OFFLOAD_SIZE; 965 error = hn_rndis_query2(sc, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES, 966 &in, size, caps, &caps_len, NDIS_OFFLOAD_SIZE_6_0); 967 if (error) 968 return (error); 969 970 /* 971 * Preliminary verification. 972 */ 973 if (caps->ndis_hdr.ndis_type != NDIS_OBJTYPE_OFFLOAD) { 974 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n", 975 caps->ndis_hdr.ndis_type); 976 return (EINVAL); 977 } 978 if (caps->ndis_hdr.ndis_rev < NDIS_OFFLOAD_REV_1) { 979 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n", 980 caps->ndis_hdr.ndis_rev); 981 return (EINVAL); 982 } 983 if (caps->ndis_hdr.ndis_size > caps_len) { 984 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, " 985 "data size %zu\n", caps->ndis_hdr.ndis_size, caps_len); 986 return (EINVAL); 987 } else if (caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE_6_0) { 988 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n", 989 caps->ndis_hdr.ndis_size); 990 return (EINVAL); 991 } else if (caps->ndis_hdr.ndis_rev >= NDIS_OFFLOAD_REV_3 && 992 caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE) { 993 if_printf(sc->hn_ifp, "invalid NDIS rev3 objsize %u\n", 994 caps->ndis_hdr.ndis_size); 995 return (EINVAL); 996 } 997 998 if (bootverbose) { 999 /* 1000 * NOTE: 1001 * caps->ndis_hdr.ndis_size MUST be checked before accessing 1002 * NDIS 6.1+ specific fields. 1003 */ 1004 if_printf(sc->hn_ifp, "hwcaps rev %u\n", 1005 caps->ndis_hdr.ndis_rev); 1006 1007 if_printf(sc->hn_ifp, "hwcaps csum: " 1008 "ip4 tx 0x%x/0x%x rx 0x%x/0x%x, " 1009 "ip6 tx 0x%x/0x%x rx 0x%x/0x%x\n", 1010 caps->ndis_csum.ndis_ip4_txcsum, 1011 caps->ndis_csum.ndis_ip4_txenc, 1012 caps->ndis_csum.ndis_ip4_rxcsum, 1013 caps->ndis_csum.ndis_ip4_rxenc, 1014 caps->ndis_csum.ndis_ip6_txcsum, 1015 caps->ndis_csum.ndis_ip6_txenc, 1016 caps->ndis_csum.ndis_ip6_rxcsum, 1017 caps->ndis_csum.ndis_ip6_rxenc); 1018 if_printf(sc->hn_ifp, "hwcaps lsov2: " 1019 "ip4 maxsz %u minsg %u encap 0x%x, " 1020 "ip6 maxsz %u minsg %u encap 0x%x opts 0x%x\n", 1021 caps->ndis_lsov2.ndis_ip4_maxsz, 1022 caps->ndis_lsov2.ndis_ip4_minsg, 1023 caps->ndis_lsov2.ndis_ip4_encap, 1024 caps->ndis_lsov2.ndis_ip6_maxsz, 1025 caps->ndis_lsov2.ndis_ip6_minsg, 1026 caps->ndis_lsov2.ndis_ip6_encap, 1027 caps->ndis_lsov2.ndis_ip6_opts); 1028 if (caps->ndis_hdr.ndis_rev >= NDIS_OFFLOAD_REV_3) 1029 if_printf(sc->hn_ifp, "hwcaps rsc: " 1030 "ip4 %u ip6 %u\n", 1031 caps->ndis_rsc.ndis_ip4, 1032 caps->ndis_rsc.ndis_ip6); 1033 } 1034 return (0); 1035 } 1036 1037 int 1038 hn_rndis_attach(struct hn_softc *sc, int mtu, int *init_done) 1039 { 1040 int error; 1041 1042 *init_done = 0; 1043 1044 /* 1045 * Initialize RNDIS. 1046 */ 1047 error = hn_rndis_init(sc); 1048 if (error) 1049 return (error); 1050 *init_done = 1; 1051 1052 /* 1053 * Configure NDIS offload settings. 1054 */ 1055 hn_rndis_conf_offload(sc, mtu); 1056 return (0); 1057 } 1058 1059 void 1060 hn_rndis_detach(struct hn_softc *sc) 1061 { 1062 1063 /* Halt the RNDIS. */ 1064 hn_rndis_halt(sc); 1065 } 1066