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