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