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