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