2 * Copyright (c) 2009-2012,2016-2017 Microsoft Corp.
3 * Copyright (c) 2010-2012 Citrix Inc.
4 * Copyright (c) 2012 NetApp Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
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.
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.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include "opt_inet6.h"
35 #include <sys/param.h>
36 #include <sys/socket.h>
37 #include <sys/systm.h>
38 #include <sys/taskqueue.h>
40 #include <machine/atomic.h>
42 #include <net/ethernet.h>
44 #include <net/if_arp.h>
45 #include <net/if_var.h>
46 #include <net/if_media.h>
47 #include <net/rndis.h>
49 #include <netinet/in.h>
50 #include <netinet/ip.h>
51 #include <netinet/tcp_lro.h>
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>
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>
64 #define HN_RNDIS_RID_COMPAT_MASK 0xffff
65 #define HN_RNDIS_RID_COMPAT_MAX HN_RNDIS_RID_COMPAT_MASK
67 #define HN_RNDIS_XFER_SIZE 2048
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)
81 static const void *hn_rndis_xact_exec1(struct hn_softc *,
82 struct vmbus_xact *, size_t,
83 struct hn_nvs_sendctx *, size_t *);
84 static const void *hn_rndis_xact_execute(struct hn_softc *,
85 struct vmbus_xact *, uint32_t, size_t, size_t *,
87 static int hn_rndis_query(struct hn_softc *, uint32_t,
88 const void *, size_t, void *, size_t *);
89 static int hn_rndis_query2(struct hn_softc *, uint32_t,
90 const void *, size_t, void *, size_t *, size_t);
91 static int hn_rndis_set(struct hn_softc *, uint32_t,
92 const void *, size_t);
93 static int hn_rndis_init(struct hn_softc *);
94 static int hn_rndis_halt(struct hn_softc *);
95 static int hn_rndis_conf_offload(struct hn_softc *, int);
96 static int hn_rndis_query_hwcaps(struct hn_softc *,
97 struct ndis_offload *);
99 static __inline uint32_t
100 hn_rndis_rid(struct hn_softc *sc)
105 rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1);
109 /* Use upper 16 bits for non-compat RNDIS messages. */
110 return ((rid & 0xffff) << 16);
114 hn_rndis_rx_ctrl(struct hn_softc *sc, const void *data, int dlen)
116 const struct rndis_comp_hdr *comp;
117 const struct rndis_msghdr *hdr;
119 KASSERT(dlen >= sizeof(*hdr), ("invalid RNDIS msg\n"));
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");
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);
138 case REMOTE_NDIS_RESET_CMPLT:
140 * Reset completed, no rid.
143 * RESET is not issued by hn(4), so this message should
146 if_printf(sc->hn_ifp, "RESET cmplt received\n");
150 if_printf(sc->hn_ifp, "unknown RNDIS msg 0x%x\n",
157 hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr)
162 eaddr_len = ETHER_ADDR_LEN;
163 error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0,
167 if (eaddr_len != ETHER_ADDR_LEN) {
168 if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len);
175 hn_rndis_get_linkstatus(struct hn_softc *sc, uint32_t *link_status)
180 size = sizeof(*link_status);
181 error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0,
185 if (size != sizeof(uint32_t)) {
186 if_printf(sc->hn_ifp, "invalid link status len %zu\n", size);
193 hn_rndis_get_mtu(struct hn_softc *sc, uint32_t *mtu)
199 error = hn_rndis_query(sc, OID_GEN_MAXIMUM_FRAME_SIZE, NULL, 0,
203 if (size != sizeof(uint32_t)) {
204 if_printf(sc->hn_ifp, "invalid mtu len %zu\n", size);
211 hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen,
212 struct hn_nvs_sendctx *sndc, size_t *comp_len)
214 struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT];
218 KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0,
219 ("invalid request length %zu", reqlen));
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) {
235 gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt;
236 gpa[gpa_cnt].gpa_len = len;
237 gpa[gpa_cnt].gpa_ofs = 0;
241 KASSERT(reqlen == 0, ("still have %zu request data left", reqlen));
244 * Send this RNDIS control message and wait for its completion
247 vmbus_xact_activate(xact);
248 error = hn_nvs_send_rndis_ctrl(sc->hn_prichan, sndc, gpa, gpa_cnt);
250 vmbus_xact_deactivate(xact);
251 if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error);
254 return (vmbus_chan_xact_wait(sc->hn_prichan, xact, comp_len,
259 hn_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)
262 const struct rndis_comp_hdr *comp;
263 size_t comp_len, min_complen = *comp_len0;
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));
270 * Execute the xact setup by the caller.
272 comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_nvs_sendctx_none,
278 * Check this RNDIS complete message.
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);
286 if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n",
291 if (comp->rm_len < min_complen) {
292 if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n",
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);
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);
307 *comp_len0 = comp_len;
312 hn_rndis_query(struct hn_softc *sc, uint32_t oid,
313 const void *idata, size_t idlen, void *odata, size_t *odlen0)
316 return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0));
320 hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
321 const void *idata, size_t idlen, void *odata, size_t *odlen0,
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;
331 reqlen = sizeof(*req) + idlen;
332 xact = vmbus_xact_get(sc->hn_xact, reqlen);
334 if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid);
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;
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."
349 * If this field was set to 0 according to the RNDIS Spec,
350 * Hyper-V would set non-SUCCESS status in the query
353 req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET;
356 req->rm_infobuflen = idlen;
357 /* Input data immediately follows RNDIS query. */
358 memcpy(req + 1, idata, idlen);
361 comp_len = sizeof(*comp) + min_odlen;
362 comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
363 REMOTE_NDIS_QUERY_CMPLT);
365 if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid);
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);
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);
385 * Check output data length and offset.
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);
399 if (comp->rm_infobuflen < odlen)
400 odlen = comp->rm_infobuflen;
401 memcpy(odata, ((const uint8_t *)comp) + ofs, odlen);
406 vmbus_xact_put(xact);
411 hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0)
413 struct ndis_rss_caps in, caps;
415 int error, indsz, rxr_cnt, hash_fnidx;
416 uint32_t hash_func = 0, hash_types = 0;
420 if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20)
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;
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);
435 * Preliminary verification.
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);
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);
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);
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);
458 * Save information for later RSS configuration.
460 if (caps.ndis_nrxr == 0) {
461 if_printf(sc->hn_ifp, "0 RX rings!?\n");
465 if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr);
466 rxr_cnt = caps.ndis_nrxr;
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",
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);
482 if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
485 indsz = caps.ndis_nind;
487 indsz = NDIS_HASH_INDCNT;
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);
497 * Toeplitz is at the lowest bit, and it is prefered; so ffs(),
498 * instead of fls(), is used here.
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",
506 hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */
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",
520 if_printf(sc->hn_ifp, "RSS caps %#x\n", caps.ndis_caps);
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;
534 hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen)
536 struct rndis_set_req *req;
537 const struct rndis_set_comp *comp;
538 struct vmbus_xact *xact;
539 size_t reqlen, comp_len;
543 KASSERT(dlen > 0, ("invalid dlen %zu", dlen));
545 reqlen = sizeof(*req) + dlen;
546 xact = vmbus_xact_get(sc->hn_xact, reqlen);
548 if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid);
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;
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);
562 comp_len = sizeof(*comp);
563 comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
564 REMOTE_NDIS_SET_CMPLT);
566 if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid);
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);
579 vmbus_xact_put(xact);
584 hn_rndis_conf_offload(struct hn_softc *sc, int mtu)
586 struct ndis_offload hwcaps;
587 struct ndis_offload_params params;
590 int error, tso_maxsz, tso_minsg;
592 error = hn_rndis_query_hwcaps(sc, &hwcaps);
594 if_printf(sc->hn_ifp, "hwcaps query failed: %d\n", error);
598 /* NOTE: 0 means "no change" */
599 memset(¶ms, 0, sizeof(params));
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;
606 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3;
607 paramsz = NDIS_OFFLOAD_PARAMS_SIZE;
609 params.ndis_hdr.ndis_size = paramsz;
614 tso_maxsz = IP_MAXPACKET;
616 if (hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
618 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON;
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;
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) {
629 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON;
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;
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;
652 sc->hn_ndis_tso_szmax = tso_maxsz;
653 sc->hn_ndis_tso_sgmin = tso_minsg;
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);
664 if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4) ==
665 HN_NDIS_TXCSUM_CAP_IP4) {
667 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TX;
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;
673 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_RX;
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;
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;
686 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_RX;
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;
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;
698 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_RX;
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;
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;
711 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_RX;
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;
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;
724 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_RX;
728 if_printf(sc->hn_ifp, "offload csum: "
729 "ip4 %u, tcp4 %u, udp4 %u, tcp6 %u, udp6 %u\n",
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);
740 error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, ¶ms, paramsz);
742 if_printf(sc->hn_ifp, "offload config failed: %d\n", error);
747 if_printf(sc->hn_ifp, "offload config done\n");
753 hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags)
755 struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
756 struct ndis_rss_params *prm = &rss->rss_params;
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.
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));
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"));
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);
781 * DO NOT whack rss_key and rss_ind, which are setup by the caller.
783 memset(prm, 0, sizeof(*prm));
784 rss_size = NDIS_RSSPRM_TOEPLITZ_SIZE(sc->hn_rss_ind_size);
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]);
799 error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS,
802 if_printf(sc->hn_ifp, "RSS config failed: %d\n", error);
805 if_printf(sc->hn_ifp, "RSS config done\n");
811 hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter)
815 error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER,
816 &filter, sizeof(filter));
818 if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n",
822 if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n",
830 hn_rndis_init(struct hn_softc *sc)
832 struct rndis_init_req *req;
833 const struct rndis_init_comp *comp;
834 struct vmbus_xact *xact;
839 xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
841 if_printf(sc->hn_ifp, "no xact for RNDIS init\n");
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);
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;
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);
857 if_printf(sc->hn_ifp, "exec RNDIS init failed\n");
862 if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
863 if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n",
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;
872 if (sc->hn_rndis_agg_align < sizeof(uint32_t)) {
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
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);
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);
893 vmbus_xact_put(xact);
898 hn_rndis_halt(struct hn_softc *sc)
900 struct vmbus_xact *xact;
901 struct rndis_halt_req *halt;
902 struct hn_nvs_sendctx sndc;
905 xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt));
907 if_printf(sc->hn_ifp, "no xact for RNDIS halt\n");
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);
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);
919 vmbus_xact_put(xact);
921 if_printf(sc->hn_ifp, "RNDIS halt done\n");
926 hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps)
928 struct ndis_offload in;
929 size_t caps_len, size;
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;
941 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1;
942 size = NDIS_OFFLOAD_SIZE_6_0;
944 in.ndis_hdr.ndis_size = size;
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);
953 * Preliminary verification.
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);
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);
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);
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);
978 * caps->ndis_hdr.ndis_size MUST be checked before accessing
979 * NDIS 6.1+ specific fields.
981 if_printf(sc->hn_ifp, "hwcaps rev %u\n",
982 caps->ndis_hdr.ndis_rev);
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);
1010 hn_rndis_attach(struct hn_softc *sc, int mtu, int *init_done)
1019 error = hn_rndis_init(sc);
1025 * Configure NDIS offload settings.
1027 hn_rndis_conf_offload(sc, mtu);
1032 hn_rndis_detach(struct hn_softc *sc)
1035 /* Halt the RNDIS. */