1 /*- 2 * Copyright (c) 2009-2012,2016 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 static const void * 192 hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen, 193 struct hn_nvs_sendctx *sndc, size_t *comp_len) 194 { 195 struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT]; 196 int gpa_cnt, error; 197 bus_addr_t paddr; 198 199 KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0, 200 ("invalid request length %zu", reqlen)); 201 202 /* 203 * Setup the SG list. 204 */ 205 paddr = vmbus_xact_req_paddr(xact); 206 KASSERT((paddr & PAGE_MASK) == 0, 207 ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr)); 208 for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) { 209 int len = PAGE_SIZE; 210 211 if (reqlen == 0) 212 break; 213 if (reqlen < len) 214 len = reqlen; 215 216 gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt; 217 gpa[gpa_cnt].gpa_len = len; 218 gpa[gpa_cnt].gpa_ofs = 0; 219 220 reqlen -= len; 221 } 222 KASSERT(reqlen == 0, ("still have %zu request data left", reqlen)); 223 224 /* 225 * Send this RNDIS control message and wait for its completion 226 * message. 227 */ 228 vmbus_xact_activate(xact); 229 error = hn_nvs_send_rndis_ctrl(sc->hn_prichan, sndc, gpa, gpa_cnt); 230 if (error) { 231 vmbus_xact_deactivate(xact); 232 if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error); 233 return (NULL); 234 } 235 return (vmbus_xact_wait(xact, comp_len)); 236 } 237 238 static const void * 239 hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid, 240 size_t reqlen, size_t *comp_len0, uint32_t comp_type) 241 { 242 const struct rndis_comp_hdr *comp; 243 size_t comp_len, min_complen = *comp_len0; 244 245 KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid)); 246 KASSERT(min_complen >= sizeof(*comp), 247 ("invalid minimum complete len %zu", min_complen)); 248 249 /* 250 * Execute the xact setup by the caller. 251 */ 252 comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_nvs_sendctx_none, 253 &comp_len); 254 if (comp == NULL) 255 return (NULL); 256 257 /* 258 * Check this RNDIS complete message. 259 */ 260 if (comp_len < min_complen) { 261 if (comp_len >= sizeof(*comp)) { 262 /* rm_status field is valid */ 263 if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, " 264 "status 0x%08x\n", comp_len, comp->rm_status); 265 } else { 266 if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n", 267 comp_len); 268 } 269 return (NULL); 270 } 271 if (comp->rm_len < min_complen) { 272 if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n", 273 comp->rm_len); 274 return (NULL); 275 } 276 if (comp->rm_type != comp_type) { 277 if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, " 278 "expect 0x%08x\n", comp->rm_type, comp_type); 279 return (NULL); 280 } 281 if (comp->rm_rid != rid) { 282 if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, " 283 "expect %u\n", comp->rm_rid, rid); 284 return (NULL); 285 } 286 /* All pass! */ 287 *comp_len0 = comp_len; 288 return (comp); 289 } 290 291 static int 292 hn_rndis_query(struct hn_softc *sc, uint32_t oid, 293 const void *idata, size_t idlen, void *odata, size_t *odlen0) 294 { 295 296 return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0)); 297 } 298 299 static int 300 hn_rndis_query2(struct hn_softc *sc, uint32_t oid, 301 const void *idata, size_t idlen, void *odata, size_t *odlen0, 302 size_t min_odlen) 303 { 304 struct rndis_query_req *req; 305 const struct rndis_query_comp *comp; 306 struct vmbus_xact *xact; 307 size_t reqlen, odlen = *odlen0, comp_len; 308 int error, ofs; 309 uint32_t rid; 310 311 reqlen = sizeof(*req) + idlen; 312 xact = vmbus_xact_get(sc->hn_xact, reqlen); 313 if (xact == NULL) { 314 if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid); 315 return (ENXIO); 316 } 317 rid = hn_rndis_rid(sc); 318 req = vmbus_xact_req_data(xact); 319 req->rm_type = REMOTE_NDIS_QUERY_MSG; 320 req->rm_len = reqlen; 321 req->rm_rid = rid; 322 req->rm_oid = oid; 323 /* 324 * XXX 325 * This is _not_ RNDIS Spec conforming: 326 * "This MUST be set to 0 when there is no input data 327 * associated with the OID." 328 * 329 * If this field was set to 0 according to the RNDIS Spec, 330 * Hyper-V would set non-SUCCESS status in the query 331 * completion. 332 */ 333 req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET; 334 335 if (idlen > 0) { 336 req->rm_infobuflen = idlen; 337 /* Input data immediately follows RNDIS query. */ 338 memcpy(req + 1, idata, idlen); 339 } 340 341 comp_len = sizeof(*comp) + min_odlen; 342 comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len, 343 REMOTE_NDIS_QUERY_CMPLT); 344 if (comp == NULL) { 345 if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid); 346 error = EIO; 347 goto done; 348 } 349 350 if (comp->rm_status != RNDIS_STATUS_SUCCESS) { 351 if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: " 352 "status 0x%08x\n", oid, comp->rm_status); 353 error = EIO; 354 goto done; 355 } 356 if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) { 357 /* No output data! */ 358 if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid); 359 *odlen0 = 0; 360 error = 0; 361 goto done; 362 } 363 364 /* 365 * Check output data length and offset. 366 */ 367 /* ofs is the offset from the beginning of comp. */ 368 ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset); 369 if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) { 370 if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, " 371 "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen); 372 error = EINVAL; 373 goto done; 374 } 375 376 /* 377 * Save output data. 378 */ 379 if (comp->rm_infobuflen < odlen) 380 odlen = comp->rm_infobuflen; 381 memcpy(odata, ((const uint8_t *)comp) + ofs, odlen); 382 *odlen0 = odlen; 383 384 error = 0; 385 done: 386 vmbus_xact_put(xact); 387 return (error); 388 } 389 390 int 391 hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0) 392 { 393 struct ndis_rss_caps in, caps; 394 size_t caps_len; 395 int error, indsz, rxr_cnt, hash_fnidx; 396 uint32_t hash_func = 0, hash_types = 0; 397 398 *rxr_cnt0 = 0; 399 400 if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20) 401 return (EOPNOTSUPP); 402 403 memset(&in, 0, sizeof(in)); 404 in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS; 405 in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2; 406 in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE; 407 408 caps_len = NDIS_RSS_CAPS_SIZE; 409 error = hn_rndis_query2(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES, 410 &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len, NDIS_RSS_CAPS_SIZE_6_0); 411 if (error) 412 return (error); 413 414 /* 415 * Preliminary verification. 416 */ 417 if (caps.ndis_hdr.ndis_type != NDIS_OBJTYPE_RSS_CAPS) { 418 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n", 419 caps.ndis_hdr.ndis_type); 420 return (EINVAL); 421 } 422 if (caps.ndis_hdr.ndis_rev < NDIS_RSS_CAPS_REV_1) { 423 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n", 424 caps.ndis_hdr.ndis_rev); 425 return (EINVAL); 426 } 427 if (caps.ndis_hdr.ndis_size > caps_len) { 428 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, " 429 "data size %zu\n", caps.ndis_hdr.ndis_size, caps_len); 430 return (EINVAL); 431 } else if (caps.ndis_hdr.ndis_size < NDIS_RSS_CAPS_SIZE_6_0) { 432 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n", 433 caps.ndis_hdr.ndis_size); 434 return (EINVAL); 435 } 436 437 /* 438 * Save information for later RSS configuration. 439 */ 440 if (caps.ndis_nrxr == 0) { 441 if_printf(sc->hn_ifp, "0 RX rings!?\n"); 442 return (EINVAL); 443 } 444 if (bootverbose) 445 if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr); 446 rxr_cnt = caps.ndis_nrxr; 447 448 if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE && 449 caps.ndis_hdr.ndis_rev >= NDIS_RSS_CAPS_REV_2) { 450 if (caps.ndis_nind > NDIS_HASH_INDCNT) { 451 if_printf(sc->hn_ifp, 452 "too many RSS indirect table entries %u\n", 453 caps.ndis_nind); 454 return (EOPNOTSUPP); 455 } 456 if (!powerof2(caps.ndis_nind)) { 457 if_printf(sc->hn_ifp, "RSS indirect table size is not " 458 "power-of-2 %u\n", caps.ndis_nind); 459 } 460 461 if (bootverbose) { 462 if_printf(sc->hn_ifp, "RSS indirect table size %u\n", 463 caps.ndis_nind); 464 } 465 indsz = caps.ndis_nind; 466 } else { 467 indsz = NDIS_HASH_INDCNT; 468 } 469 if (indsz < rxr_cnt) { 470 if_printf(sc->hn_ifp, "# of RX rings (%d) > " 471 "RSS indirect table size %d\n", rxr_cnt, indsz); 472 rxr_cnt = indsz; 473 } 474 475 /* 476 * NOTE: 477 * Toeplitz is at the lowest bit, and it is prefered; so ffs(), 478 * instead of fls(), is used here. 479 */ 480 hash_fnidx = ffs(caps.ndis_caps & NDIS_RSS_CAP_HASHFUNC_MASK); 481 if (hash_fnidx == 0) { 482 if_printf(sc->hn_ifp, "no hash functions, caps 0x%08x\n", 483 caps.ndis_caps); 484 return (EOPNOTSUPP); 485 } 486 hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */ 487 488 if (caps.ndis_caps & NDIS_RSS_CAP_IPV4) 489 hash_types |= NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4; 490 if (caps.ndis_caps & NDIS_RSS_CAP_IPV6) 491 hash_types |= NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6; 492 if (caps.ndis_caps & NDIS_RSS_CAP_IPV6_EX) 493 hash_types |= NDIS_HASH_IPV6_EX | NDIS_HASH_TCP_IPV6_EX; 494 if (hash_types == 0) { 495 if_printf(sc->hn_ifp, "no hash types, caps 0x%08x\n", 496 caps.ndis_caps); 497 return (EOPNOTSUPP); 498 } 499 500 /* Commit! */ 501 sc->hn_rss_ind_size = indsz; 502 sc->hn_rss_hash = hash_func | hash_types; 503 *rxr_cnt0 = rxr_cnt; 504 return (0); 505 } 506 507 static int 508 hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen) 509 { 510 struct rndis_set_req *req; 511 const struct rndis_set_comp *comp; 512 struct vmbus_xact *xact; 513 size_t reqlen, comp_len; 514 uint32_t rid; 515 int error; 516 517 KASSERT(dlen > 0, ("invalid dlen %zu", dlen)); 518 519 reqlen = sizeof(*req) + dlen; 520 xact = vmbus_xact_get(sc->hn_xact, reqlen); 521 if (xact == NULL) { 522 if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid); 523 return (ENXIO); 524 } 525 rid = hn_rndis_rid(sc); 526 req = vmbus_xact_req_data(xact); 527 req->rm_type = REMOTE_NDIS_SET_MSG; 528 req->rm_len = reqlen; 529 req->rm_rid = rid; 530 req->rm_oid = oid; 531 req->rm_infobuflen = dlen; 532 req->rm_infobufoffset = RNDIS_SET_REQ_INFOBUFOFFSET; 533 /* Data immediately follows RNDIS set. */ 534 memcpy(req + 1, data, dlen); 535 536 comp_len = sizeof(*comp); 537 comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len, 538 REMOTE_NDIS_SET_CMPLT); 539 if (comp == NULL) { 540 if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid); 541 error = EIO; 542 goto done; 543 } 544 545 if (comp->rm_status != RNDIS_STATUS_SUCCESS) { 546 if_printf(sc->hn_ifp, "RNDIS set 0x%08x failed: " 547 "status 0x%08x\n", oid, comp->rm_status); 548 error = EIO; 549 goto done; 550 } 551 error = 0; 552 done: 553 vmbus_xact_put(xact); 554 return (error); 555 } 556 557 static int 558 hn_rndis_conf_offload(struct hn_softc *sc, int mtu) 559 { 560 struct ndis_offload hwcaps; 561 struct ndis_offload_params params; 562 uint32_t caps = 0; 563 size_t paramsz; 564 int error, tso_maxsz, tso_minsg; 565 566 error = hn_rndis_query_hwcaps(sc, &hwcaps); 567 if (error) { 568 if_printf(sc->hn_ifp, "hwcaps query failed: %d\n", error); 569 return (error); 570 } 571 572 /* NOTE: 0 means "no change" */ 573 memset(¶ms, 0, sizeof(params)); 574 575 params.ndis_hdr.ndis_type = NDIS_OBJTYPE_DEFAULT; 576 if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30) { 577 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_2; 578 paramsz = NDIS_OFFLOAD_PARAMS_SIZE_6_1; 579 } else { 580 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3; 581 paramsz = NDIS_OFFLOAD_PARAMS_SIZE; 582 } 583 params.ndis_hdr.ndis_size = paramsz; 584 585 /* 586 * TSO4/TSO6 setup. 587 */ 588 tso_maxsz = IP_MAXPACKET; 589 tso_minsg = 2; 590 if (hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) { 591 caps |= HN_CAP_TSO4; 592 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON; 593 594 if (hwcaps.ndis_lsov2.ndis_ip4_maxsz < tso_maxsz) 595 tso_maxsz = hwcaps.ndis_lsov2.ndis_ip4_maxsz; 596 if (hwcaps.ndis_lsov2.ndis_ip4_minsg > tso_minsg) 597 tso_minsg = hwcaps.ndis_lsov2.ndis_ip4_minsg; 598 } 599 if ((hwcaps.ndis_lsov2.ndis_ip6_encap & NDIS_OFFLOAD_ENCAP_8023) && 600 (hwcaps.ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6) == 601 HN_NDIS_LSOV2_CAP_IP6) { 602 #ifdef notyet 603 caps |= HN_CAP_TSO6; 604 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON; 605 606 if (hwcaps.ndis_lsov2.ndis_ip6_maxsz < tso_maxsz) 607 tso_maxsz = hwcaps.ndis_lsov2.ndis_ip6_maxsz; 608 if (hwcaps.ndis_lsov2.ndis_ip6_minsg > tso_minsg) 609 tso_minsg = hwcaps.ndis_lsov2.ndis_ip6_minsg; 610 #endif 611 } 612 sc->hn_ndis_tso_szmax = 0; 613 sc->hn_ndis_tso_sgmin = 0; 614 if (caps & (HN_CAP_TSO4 | HN_CAP_TSO6)) { 615 KASSERT(tso_maxsz <= IP_MAXPACKET, 616 ("invalid NDIS TSO maxsz %d", tso_maxsz)); 617 KASSERT(tso_minsg >= 2, 618 ("invalid NDIS TSO minsg %d", tso_minsg)); 619 if (tso_maxsz < tso_minsg * mtu) { 620 if_printf(sc->hn_ifp, "invalid NDIS TSO config: " 621 "maxsz %d, minsg %d, mtu %d; " 622 "disable TSO4 and TSO6\n", 623 tso_maxsz, tso_minsg, mtu); 624 caps &= ~(HN_CAP_TSO4 | HN_CAP_TSO6); 625 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_OFF; 626 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_OFF; 627 } else { 628 sc->hn_ndis_tso_szmax = tso_maxsz; 629 sc->hn_ndis_tso_sgmin = tso_minsg; 630 if (bootverbose) { 631 if_printf(sc->hn_ifp, "NDIS TSO " 632 "szmax %d sgmin %d\n", 633 sc->hn_ndis_tso_szmax, 634 sc->hn_ndis_tso_sgmin); 635 } 636 } 637 } 638 639 /* IPv4 checksum */ 640 if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4) == 641 HN_NDIS_TXCSUM_CAP_IP4) { 642 caps |= HN_CAP_IPCS; 643 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TX; 644 } 645 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4) { 646 if (params.ndis_ip4csum == NDIS_OFFLOAD_PARAM_TX) 647 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX; 648 else 649 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_RX; 650 } 651 652 /* TCP4 checksum */ 653 if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_TCP4) == 654 HN_NDIS_TXCSUM_CAP_TCP4) { 655 caps |= HN_CAP_TCP4CS; 656 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TX; 657 } 658 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) { 659 if (params.ndis_tcp4csum == NDIS_OFFLOAD_PARAM_TX) 660 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX; 661 else 662 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_RX; 663 } 664 665 /* UDP4 checksum */ 666 if (hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) { 667 caps |= HN_CAP_UDP4CS; 668 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TX; 669 } 670 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) { 671 if (params.ndis_udp4csum == NDIS_OFFLOAD_PARAM_TX) 672 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX; 673 else 674 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_RX; 675 } 676 677 /* TCP6 checksum */ 678 if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_TCP6) == 679 HN_NDIS_TXCSUM_CAP_TCP6) { 680 caps |= HN_CAP_TCP6CS; 681 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TX; 682 } 683 if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6) { 684 if (params.ndis_tcp6csum == NDIS_OFFLOAD_PARAM_TX) 685 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX; 686 else 687 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_RX; 688 } 689 690 /* UDP6 checksum */ 691 if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_UDP6) == 692 HN_NDIS_TXCSUM_CAP_UDP6) { 693 caps |= HN_CAP_UDP6CS; 694 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TX; 695 } 696 if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6) { 697 if (params.ndis_udp6csum == NDIS_OFFLOAD_PARAM_TX) 698 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX; 699 else 700 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_RX; 701 } 702 703 if (bootverbose) { 704 if_printf(sc->hn_ifp, "offload csum: " 705 "ip4 %u, tcp4 %u, udp4 %u, tcp6 %u, udp6 %u\n", 706 params.ndis_ip4csum, 707 params.ndis_tcp4csum, 708 params.ndis_udp4csum, 709 params.ndis_tcp6csum, 710 params.ndis_udp6csum); 711 if_printf(sc->hn_ifp, "offload lsov2: ip4 %u, ip6 %u\n", 712 params.ndis_lsov2_ip4, 713 params.ndis_lsov2_ip6); 714 } 715 716 error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, ¶ms, paramsz); 717 if (error) { 718 if_printf(sc->hn_ifp, "offload config failed: %d\n", error); 719 return (error); 720 } 721 722 if (bootverbose) 723 if_printf(sc->hn_ifp, "offload config done\n"); 724 sc->hn_caps |= caps; 725 return (0); 726 } 727 728 int 729 hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags) 730 { 731 struct ndis_rssprm_toeplitz *rss = &sc->hn_rss; 732 struct ndis_rss_params *prm = &rss->rss_params; 733 int error, rss_size; 734 735 /* 736 * Only NDIS 6.20+ is supported: 737 * We only support 4bytes element in indirect table, which has been 738 * adopted since NDIS 6.20. 739 */ 740 KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_20, 741 ("NDIS 6.20+ is required, NDIS version 0x%08x", sc->hn_ndis_ver)); 742 743 /* XXX only one can be specified through, popcnt? */ 744 KASSERT((sc->hn_rss_hash & NDIS_HASH_FUNCTION_MASK), ("no hash func")); 745 KASSERT((sc->hn_rss_hash & NDIS_HASH_TYPE_MASK), ("no hash types")); 746 KASSERT(sc->hn_rss_ind_size > 0, ("no indirect table size")); 747 748 if (bootverbose) { 749 if_printf(sc->hn_ifp, "RSS indirect table size %d, " 750 "hash 0x%08x\n", sc->hn_rss_ind_size, sc->hn_rss_hash); 751 } 752 753 /* 754 * NOTE: 755 * DO NOT whack rss_key and rss_ind, which are setup by the caller. 756 */ 757 memset(prm, 0, sizeof(*prm)); 758 rss_size = NDIS_RSSPRM_TOEPLITZ_SIZE(sc->hn_rss_ind_size); 759 760 prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS; 761 prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2; 762 prm->ndis_hdr.ndis_size = rss_size; 763 prm->ndis_flags = flags; 764 prm->ndis_hash = sc->hn_rss_hash; 765 prm->ndis_indsize = sizeof(rss->rss_ind[0]) * sc->hn_rss_ind_size; 766 prm->ndis_indoffset = 767 __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]); 768 prm->ndis_keysize = sizeof(rss->rss_key); 769 prm->ndis_keyoffset = 770 __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]); 771 772 error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS, 773 rss, rss_size); 774 if (error) { 775 if_printf(sc->hn_ifp, "RSS config failed: %d\n", error); 776 } else { 777 if (bootverbose) 778 if_printf(sc->hn_ifp, "RSS config done\n"); 779 } 780 return (error); 781 } 782 783 int 784 hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter) 785 { 786 int error; 787 788 error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER, 789 &filter, sizeof(filter)); 790 if (error) { 791 if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n", 792 filter, error); 793 } else { 794 if (bootverbose) { 795 if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n", 796 filter); 797 } 798 } 799 return (error); 800 } 801 802 static int 803 hn_rndis_init(struct hn_softc *sc) 804 { 805 struct rndis_init_req *req; 806 const struct rndis_init_comp *comp; 807 struct vmbus_xact *xact; 808 size_t comp_len; 809 uint32_t rid; 810 int error; 811 812 xact = vmbus_xact_get(sc->hn_xact, sizeof(*req)); 813 if (xact == NULL) { 814 if_printf(sc->hn_ifp, "no xact for RNDIS init\n"); 815 return (ENXIO); 816 } 817 rid = hn_rndis_rid(sc); 818 req = vmbus_xact_req_data(xact); 819 req->rm_type = REMOTE_NDIS_INITIALIZE_MSG; 820 req->rm_len = sizeof(*req); 821 req->rm_rid = rid; 822 req->rm_ver_major = RNDIS_VERSION_MAJOR; 823 req->rm_ver_minor = RNDIS_VERSION_MINOR; 824 req->rm_max_xfersz = HN_RNDIS_XFER_SIZE; 825 826 comp_len = RNDIS_INIT_COMP_SIZE_MIN; 827 comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len, 828 REMOTE_NDIS_INITIALIZE_CMPLT); 829 if (comp == NULL) { 830 if_printf(sc->hn_ifp, "exec RNDIS init failed\n"); 831 error = EIO; 832 goto done; 833 } 834 835 if (comp->rm_status != RNDIS_STATUS_SUCCESS) { 836 if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n", 837 comp->rm_status); 838 error = EIO; 839 goto done; 840 } 841 if (bootverbose) { 842 if_printf(sc->hn_ifp, "RNDIS ver %u.%u, pktsz %u, pktcnt %u, " 843 "align %u\n", comp->rm_ver_major, comp->rm_ver_minor, 844 comp->rm_pktmaxsz, comp->rm_pktmaxcnt, 845 1U << comp->rm_align); 846 } 847 error = 0; 848 done: 849 vmbus_xact_put(xact); 850 return (error); 851 } 852 853 static int 854 hn_rndis_halt(struct hn_softc *sc) 855 { 856 struct vmbus_xact *xact; 857 struct rndis_halt_req *halt; 858 struct hn_nvs_sendctx sndc; 859 size_t comp_len; 860 861 xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt)); 862 if (xact == NULL) { 863 if_printf(sc->hn_ifp, "no xact for RNDIS halt\n"); 864 return (ENXIO); 865 } 866 halt = vmbus_xact_req_data(xact); 867 halt->rm_type = REMOTE_NDIS_HALT_MSG; 868 halt->rm_len = sizeof(*halt); 869 halt->rm_rid = hn_rndis_rid(sc); 870 871 /* No RNDIS completion; rely on NVS message send completion */ 872 hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact); 873 hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len); 874 875 vmbus_xact_put(xact); 876 if (bootverbose) 877 if_printf(sc->hn_ifp, "RNDIS halt done\n"); 878 return (0); 879 } 880 881 static int 882 hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps) 883 { 884 struct ndis_offload in; 885 size_t caps_len, size; 886 int error; 887 888 memset(&in, 0, sizeof(in)); 889 in.ndis_hdr.ndis_type = NDIS_OBJTYPE_OFFLOAD; 890 if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) { 891 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_3; 892 size = NDIS_OFFLOAD_SIZE; 893 } else if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_1) { 894 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_2; 895 size = NDIS_OFFLOAD_SIZE_6_1; 896 } else { 897 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1; 898 size = NDIS_OFFLOAD_SIZE_6_0; 899 } 900 in.ndis_hdr.ndis_size = size; 901 902 caps_len = NDIS_OFFLOAD_SIZE; 903 error = hn_rndis_query2(sc, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES, 904 &in, size, caps, &caps_len, NDIS_OFFLOAD_SIZE_6_0); 905 if (error) 906 return (error); 907 908 /* 909 * Preliminary verification. 910 */ 911 if (caps->ndis_hdr.ndis_type != NDIS_OBJTYPE_OFFLOAD) { 912 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n", 913 caps->ndis_hdr.ndis_type); 914 return (EINVAL); 915 } 916 if (caps->ndis_hdr.ndis_rev < NDIS_OFFLOAD_REV_1) { 917 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n", 918 caps->ndis_hdr.ndis_rev); 919 return (EINVAL); 920 } 921 if (caps->ndis_hdr.ndis_size > caps_len) { 922 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, " 923 "data size %zu\n", caps->ndis_hdr.ndis_size, caps_len); 924 return (EINVAL); 925 } else if (caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE_6_0) { 926 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n", 927 caps->ndis_hdr.ndis_size); 928 return (EINVAL); 929 } 930 931 if (bootverbose) { 932 /* 933 * NOTE: 934 * caps->ndis_hdr.ndis_size MUST be checked before accessing 935 * NDIS 6.1+ specific fields. 936 */ 937 if_printf(sc->hn_ifp, "hwcaps rev %u\n", 938 caps->ndis_hdr.ndis_rev); 939 940 if_printf(sc->hn_ifp, "hwcaps csum: " 941 "ip4 tx 0x%x/0x%x rx 0x%x/0x%x, " 942 "ip6 tx 0x%x/0x%x rx 0x%x/0x%x\n", 943 caps->ndis_csum.ndis_ip4_txcsum, 944 caps->ndis_csum.ndis_ip4_txenc, 945 caps->ndis_csum.ndis_ip4_rxcsum, 946 caps->ndis_csum.ndis_ip4_rxenc, 947 caps->ndis_csum.ndis_ip6_txcsum, 948 caps->ndis_csum.ndis_ip6_txenc, 949 caps->ndis_csum.ndis_ip6_rxcsum, 950 caps->ndis_csum.ndis_ip6_rxenc); 951 if_printf(sc->hn_ifp, "hwcaps lsov2: " 952 "ip4 maxsz %u minsg %u encap 0x%x, " 953 "ip6 maxsz %u minsg %u encap 0x%x opts 0x%x\n", 954 caps->ndis_lsov2.ndis_ip4_maxsz, 955 caps->ndis_lsov2.ndis_ip4_minsg, 956 caps->ndis_lsov2.ndis_ip4_encap, 957 caps->ndis_lsov2.ndis_ip6_maxsz, 958 caps->ndis_lsov2.ndis_ip6_minsg, 959 caps->ndis_lsov2.ndis_ip6_encap, 960 caps->ndis_lsov2.ndis_ip6_opts); 961 } 962 return (0); 963 } 964 965 int 966 hn_rndis_attach(struct hn_softc *sc, int mtu) 967 { 968 int error; 969 970 /* 971 * Initialize RNDIS. 972 */ 973 error = hn_rndis_init(sc); 974 if (error) 975 return (error); 976 977 /* 978 * Configure NDIS offload settings. 979 * XXX no offloading, if error happened? 980 */ 981 hn_rndis_conf_offload(sc, mtu); 982 return (0); 983 } 984 985 void 986 hn_rndis_detach(struct hn_softc *sc) 987 { 988 989 /* Halt the RNDIS. */ 990 hn_rndis_halt(sc); 991 } 992