2 * Copyright (c) 2009-2012,2016 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 <sys/param.h>
34 #include <sys/socket.h>
36 #include <sys/mutex.h>
38 #include <net/if_arp.h>
39 #include <net/if_var.h>
40 #include <net/ethernet.h>
41 #include <net/rndis.h>
42 #include <netinet/in.h>
43 #include <netinet/ip.h>
44 #include <sys/types.h>
45 #include <machine/atomic.h>
48 #include <vm/vm_param.h>
51 #include <dev/hyperv/include/hyperv.h>
52 #include <dev/hyperv/include/vmbus_xact.h>
53 #include <dev/hyperv/netvsc/hv_net_vsc.h>
54 #include <dev/hyperv/netvsc/hv_rndis_filter.h>
55 #include <dev/hyperv/netvsc/if_hnreg.h>
56 #include <dev/hyperv/netvsc/ndis.h>
58 #define HV_RF_RECVINFO_VLAN 0x1
59 #define HV_RF_RECVINFO_CSUM 0x2
60 #define HV_RF_RECVINFO_HASHINF 0x4
61 #define HV_RF_RECVINFO_HASHVAL 0x8
62 #define HV_RF_RECVINFO_ALL \
63 (HV_RF_RECVINFO_VLAN | \
64 HV_RF_RECVINFO_CSUM | \
65 HV_RF_RECVINFO_HASHINF | \
66 HV_RF_RECVINFO_HASHVAL)
68 #define HN_RNDIS_RID_COMPAT_MASK 0xffff
69 #define HN_RNDIS_RID_COMPAT_MAX HN_RNDIS_RID_COMPAT_MASK
71 #define HN_RNDIS_XFER_SIZE 2048
73 #define HN_NDIS_TXCSUM_CAP_IP4 \
74 (NDIS_TXCSUM_CAP_IP4 | NDIS_TXCSUM_CAP_IP4OPT)
75 #define HN_NDIS_TXCSUM_CAP_TCP4 \
76 (NDIS_TXCSUM_CAP_TCP4 | NDIS_TXCSUM_CAP_TCP4OPT)
77 #define HN_NDIS_TXCSUM_CAP_TCP6 \
78 (NDIS_TXCSUM_CAP_TCP6 | NDIS_TXCSUM_CAP_TCP6OPT | \
79 NDIS_TXCSUM_CAP_IP6EXT)
80 #define HN_NDIS_TXCSUM_CAP_UDP6 \
81 (NDIS_TXCSUM_CAP_UDP6 | NDIS_TXCSUM_CAP_IP6EXT)
82 #define HN_NDIS_LSOV2_CAP_IP6 \
83 (NDIS_LSOV2_CAP_IP6EXT | NDIS_LSOV2_CAP_TCP6OPT)
86 * Forward declarations
88 static void hv_rf_receive_indicate_status(struct hn_softc *sc,
89 const void *data, int dlen);
90 static void hv_rf_receive_data(struct hn_rx_ring *rxr,
91 const void *data, int dlen);
93 static int hn_rndis_query(struct hn_softc *sc, uint32_t oid,
94 const void *idata, size_t idlen, void *odata, size_t *odlen0);
95 static int hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
96 const void *idata, size_t idlen, void *odata, size_t *odlen0,
98 static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data,
100 static int hn_rndis_conf_offload(struct hn_softc *sc, int mtu);
101 static int hn_rndis_query_hwcaps(struct hn_softc *sc,
102 struct ndis_offload *caps);
104 static __inline uint32_t
105 hn_rndis_rid(struct hn_softc *sc)
110 rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1);
114 /* Use upper 16 bits for non-compat RNDIS messages. */
115 return ((rid & 0xffff) << 16);
119 hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
120 size_t pi_dlen, uint32_t pi_type)
122 const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen);
123 struct rndis_pktinfo *pi;
125 KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0,
126 ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen));
129 * Per-packet-info does not move; it only grows.
132 * rm_pktinfooffset in this phase counts from the beginning
133 * of rndis_packet_msg.
135 KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize,
136 ("%u pktinfo overflows RNDIS packet msg", pi_type));
137 pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset +
139 pkt->rm_pktinfolen += pi_size;
141 pi->rm_size = pi_size;
142 pi->rm_type = pi_type;
143 pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET;
145 /* Data immediately follow per-packet-info. */
146 pkt->rm_dataoffset += pi_size;
148 /* Update RNDIS packet msg length */
149 pkt->rm_len += pi_size;
151 return (pi->rm_data);
155 * RNDIS filter receive indicate status
158 hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen)
160 const struct rndis_status_msg *msg;
162 if (dlen < sizeof(*msg)) {
163 if_printf(sc->hn_ifp, "invalid RNDIS status\n");
168 switch (msg->rm_status) {
169 case RNDIS_STATUS_MEDIA_CONNECT:
170 netvsc_linkstatus_callback(sc, 1);
173 case RNDIS_STATUS_MEDIA_DISCONNECT:
174 netvsc_linkstatus_callback(sc, 0);
177 case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
178 /* Not really useful; ignore. */
181 case RNDIS_STATUS_NETWORK_CHANGE:
183 if_printf(sc->hn_ifp, "network changed\n");
188 if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
195 hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_recvinfo *info)
197 const struct rndis_pktinfo *pi = info_data;
200 while (info_dlen != 0) {
204 if (__predict_false(info_dlen < sizeof(*pi)))
206 if (__predict_false(info_dlen < pi->rm_size))
208 info_dlen -= pi->rm_size;
210 if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
212 if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
214 dlen = pi->rm_size - pi->rm_pktinfooffset;
217 switch (pi->rm_type) {
218 case NDIS_PKTINFO_TYPE_VLAN:
219 if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE))
221 info->vlan_info = *((const uint32_t *)data);
222 mask |= HV_RF_RECVINFO_VLAN;
225 case NDIS_PKTINFO_TYPE_CSUM:
226 if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE))
228 info->csum_info = *((const uint32_t *)data);
229 mask |= HV_RF_RECVINFO_CSUM;
232 case HN_NDIS_PKTINFO_TYPE_HASHVAL:
233 if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE))
235 info->hash_value = *((const uint32_t *)data);
236 mask |= HV_RF_RECVINFO_HASHVAL;
239 case HN_NDIS_PKTINFO_TYPE_HASHINF:
240 if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE))
242 info->hash_info = *((const uint32_t *)data);
243 mask |= HV_RF_RECVINFO_HASHINF;
250 if (mask == HV_RF_RECVINFO_ALL) {
251 /* All found; done */
255 pi = (const struct rndis_pktinfo *)
256 ((const uint8_t *)pi + pi->rm_size);
261 * - If there is no hash value, invalidate the hash info.
263 if ((mask & HV_RF_RECVINFO_HASHVAL) == 0)
264 info->hash_info = HN_NDIS_HASH_INFO_INVALID;
269 hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
272 if (off < check_off) {
273 if (__predict_true(off + len <= check_off))
275 } else if (off > check_off) {
276 if (__predict_true(check_off + check_len <= off))
283 * RNDIS filter receive data
286 hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen)
288 const struct rndis_packet_msg *pkt;
289 struct hn_recvinfo info;
290 int data_off, pktinfo_off, data_len, pktinfo_len;
295 if (__predict_false(dlen < sizeof(*pkt))) {
296 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
301 if (__predict_false(dlen < pkt->rm_len)) {
302 if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
303 "dlen %d, msglen %u\n", dlen, pkt->rm_len);
306 if (__predict_false(pkt->rm_len <
307 pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
308 if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
309 "msglen %u, data %u, oob %u, pktinfo %u\n",
310 pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
314 if (__predict_false(pkt->rm_datalen == 0)) {
315 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
322 #define IS_OFFSET_INVALID(ofs) \
323 ((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \
324 ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
326 /* XXX Hyper-V does not meet data offset alignment requirement */
327 if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
328 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
329 "data offset %u\n", pkt->rm_dataoffset);
332 if (__predict_false(pkt->rm_oobdataoffset > 0 &&
333 IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
334 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
335 "oob offset %u\n", pkt->rm_oobdataoffset);
338 if (__predict_true(pkt->rm_pktinfooffset > 0) &&
339 __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
340 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
341 "pktinfo offset %u\n", pkt->rm_pktinfooffset);
345 #undef IS_OFFSET_INVALID
347 data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
348 data_len = pkt->rm_datalen;
349 pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
350 pktinfo_len = pkt->rm_pktinfolen;
353 * Check OOB coverage.
355 if (__predict_false(pkt->rm_oobdatalen != 0)) {
356 int oob_off, oob_len;
358 if_printf(rxr->hn_ifp, "got oobdata\n");
359 oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
360 oob_len = pkt->rm_oobdatalen;
362 if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
363 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
364 "oob overflow, msglen %u, oob abs %d len %d\n",
365 pkt->rm_len, oob_off, oob_len);
370 * Check against data.
372 if (hn_rndis_check_overlap(oob_off, oob_len,
373 data_off, data_len)) {
374 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
375 "oob overlaps data, oob abs %d len %d, "
376 "data abs %d len %d\n",
377 oob_off, oob_len, data_off, data_len);
382 * Check against pktinfo.
384 if (pktinfo_len != 0 &&
385 hn_rndis_check_overlap(oob_off, oob_len,
386 pktinfo_off, pktinfo_len)) {
387 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
388 "oob overlaps pktinfo, oob abs %d len %d, "
389 "pktinfo abs %d len %d\n",
390 oob_off, oob_len, pktinfo_off, pktinfo_len);
396 * Check per-packet-info coverage and find useful per-packet-info.
398 info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
399 info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
400 info.hash_info = HN_NDIS_HASH_INFO_INVALID;
401 if (__predict_true(pktinfo_len != 0)) {
405 if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
406 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
407 "pktinfo overflow, msglen %u, "
408 "pktinfo abs %d len %d\n",
409 pkt->rm_len, pktinfo_off, pktinfo_len);
414 * Check packet info coverage.
416 overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
418 if (__predict_false(overlap)) {
419 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
420 "pktinfo overlap data, pktinfo abs %d len %d, "
421 "data abs %d len %d\n",
422 pktinfo_off, pktinfo_len, data_off, data_len);
427 * Find useful per-packet-info.
429 error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
431 if (__predict_false(error)) {
432 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
438 if (__predict_false(data_off + data_len > pkt->rm_len)) {
439 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
440 "data overflow, msglen %u, data abs %d len %d\n",
441 pkt->rm_len, data_off, data_len);
444 hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
448 * RNDIS filter on receive
451 hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
452 const void *data, int dlen)
454 const struct rndis_comp_hdr *comp;
455 const struct rndis_msghdr *hdr;
457 if (__predict_false(dlen < sizeof(*hdr))) {
458 if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
463 switch (hdr->rm_type) {
464 case REMOTE_NDIS_PACKET_MSG:
465 hv_rf_receive_data(rxr, data, dlen);
468 case REMOTE_NDIS_INITIALIZE_CMPLT:
469 case REMOTE_NDIS_QUERY_CMPLT:
470 case REMOTE_NDIS_SET_CMPLT:
471 case REMOTE_NDIS_KEEPALIVE_CMPLT: /* unused */
472 if (dlen < sizeof(*comp)) {
473 if_printf(rxr->hn_ifp, "invalid RNDIS cmplt\n");
478 KASSERT(comp->rm_rid > HN_RNDIS_RID_COMPAT_MAX,
479 ("invalid RNDIS rid 0x%08x\n", comp->rm_rid));
480 vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen);
483 case REMOTE_NDIS_INDICATE_STATUS_MSG:
484 hv_rf_receive_indicate_status(sc, data, dlen);
487 case REMOTE_NDIS_RESET_CMPLT:
489 * Reset completed, no rid.
492 * RESET is not issued by hn(4), so this message should
495 if_printf(rxr->hn_ifp, "RESET cmplt received\n");
499 if_printf(rxr->hn_ifp, "unknown RNDIS msg 0x%x\n",
506 hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr)
511 eaddr_len = ETHER_ADDR_LEN;
512 error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0,
516 if (eaddr_len != ETHER_ADDR_LEN) {
517 if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len);
524 hn_rndis_get_linkstatus(struct hn_softc *sc, uint32_t *link_status)
529 size = sizeof(*link_status);
530 error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0,
534 if (size != sizeof(uint32_t)) {
535 if_printf(sc->hn_ifp, "invalid link status len %zu\n", size);
542 hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen,
543 struct hn_send_ctx *sndc, size_t *comp_len)
545 struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT];
549 KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0,
550 ("invalid request length %zu", reqlen));
555 paddr = vmbus_xact_req_paddr(xact);
556 KASSERT((paddr & PAGE_MASK) == 0,
557 ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr));
558 for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) {
566 gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt;
567 gpa[gpa_cnt].gpa_len = len;
568 gpa[gpa_cnt].gpa_ofs = 0;
572 KASSERT(reqlen == 0, ("still have %zu request data left", reqlen));
575 * Send this RNDIS control message and wait for its completion
578 vmbus_xact_activate(xact);
579 error = hv_nv_on_send(sc->hn_prichan, HN_NVS_RNDIS_MTYPE_CTRL, sndc,
582 vmbus_xact_deactivate(xact);
583 if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error);
586 return (vmbus_xact_wait(xact, comp_len));
590 hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid,
591 size_t reqlen, size_t *comp_len0, uint32_t comp_type)
593 const struct rndis_comp_hdr *comp;
594 size_t comp_len, min_complen = *comp_len0;
596 KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid));
597 KASSERT(min_complen >= sizeof(*comp),
598 ("invalid minimum complete len %zu", min_complen));
601 * Execute the xact setup by the caller.
603 comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_send_ctx_none,
609 * Check this RNDIS complete message.
611 if (comp_len < min_complen) {
612 if (comp_len >= sizeof(*comp)) {
613 /* rm_status field is valid */
614 if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, "
615 "status 0x%08x\n", comp_len, comp->rm_status);
617 if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n",
622 if (comp->rm_len < min_complen) {
623 if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n",
627 if (comp->rm_type != comp_type) {
628 if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, "
629 "expect 0x%08x\n", comp->rm_type, comp_type);
632 if (comp->rm_rid != rid) {
633 if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, "
634 "expect %u\n", comp->rm_rid, rid);
638 *comp_len0 = comp_len;
643 hn_rndis_query(struct hn_softc *sc, uint32_t oid,
644 const void *idata, size_t idlen, void *odata, size_t *odlen0)
647 return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0));
651 hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
652 const void *idata, size_t idlen, void *odata, size_t *odlen0,
655 struct rndis_query_req *req;
656 const struct rndis_query_comp *comp;
657 struct vmbus_xact *xact;
658 size_t reqlen, odlen = *odlen0, comp_len;
662 reqlen = sizeof(*req) + idlen;
663 xact = vmbus_xact_get(sc->hn_xact, reqlen);
665 if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid);
668 rid = hn_rndis_rid(sc);
669 req = vmbus_xact_req_data(xact);
670 req->rm_type = REMOTE_NDIS_QUERY_MSG;
671 req->rm_len = reqlen;
676 * This is _not_ RNDIS Spec conforming:
677 * "This MUST be set to 0 when there is no input data
678 * associated with the OID."
680 * If this field was set to 0 according to the RNDIS Spec,
681 * Hyper-V would set non-SUCCESS status in the query
684 req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET;
687 req->rm_infobuflen = idlen;
688 /* Input data immediately follows RNDIS query. */
689 memcpy(req + 1, idata, idlen);
692 comp_len = sizeof(*comp) + min_odlen;
693 comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
694 REMOTE_NDIS_QUERY_CMPLT);
696 if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid);
701 if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
702 if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: "
703 "status 0x%08x\n", oid, comp->rm_status);
707 if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) {
708 /* No output data! */
709 if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid);
716 * Check output data length and offset.
718 /* ofs is the offset from the beginning of comp. */
719 ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset);
720 if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
721 if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
722 "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);
730 if (comp->rm_infobuflen < odlen)
731 odlen = comp->rm_infobuflen;
732 memcpy(odata, ((const uint8_t *)comp) + ofs, odlen);
737 vmbus_xact_put(xact);
742 hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt)
744 struct ndis_rss_caps in, caps;
749 * Only NDIS 6.30+ is supported.
751 KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30,
752 ("NDIS 6.30+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
755 memset(&in, 0, sizeof(in));
756 in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS;
757 in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
758 in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
760 caps_len = NDIS_RSS_CAPS_SIZE;
761 error = hn_rndis_query(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
762 &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len);
765 if (caps_len < NDIS_RSS_CAPS_SIZE_6_0) {
766 if_printf(sc->hn_ifp, "invalid NDIS RSS caps len %zu",
771 if (caps.ndis_nrxr == 0) {
772 if_printf(sc->hn_ifp, "0 RX rings!?\n");
775 *rxr_cnt = caps.ndis_nrxr;
777 if (caps_len == NDIS_RSS_CAPS_SIZE) {
779 if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
787 hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen)
789 struct rndis_set_req *req;
790 const struct rndis_set_comp *comp;
791 struct vmbus_xact *xact;
792 size_t reqlen, comp_len;
796 KASSERT(dlen > 0, ("invalid dlen %zu", dlen));
798 reqlen = sizeof(*req) + dlen;
799 xact = vmbus_xact_get(sc->hn_xact, reqlen);
801 if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid);
804 rid = hn_rndis_rid(sc);
805 req = vmbus_xact_req_data(xact);
806 req->rm_type = REMOTE_NDIS_SET_MSG;
807 req->rm_len = reqlen;
810 req->rm_infobuflen = dlen;
811 req->rm_infobufoffset = RNDIS_SET_REQ_INFOBUFOFFSET;
812 /* Data immediately follows RNDIS set. */
813 memcpy(req + 1, data, dlen);
815 comp_len = sizeof(*comp);
816 comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
817 REMOTE_NDIS_SET_CMPLT);
819 if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid);
824 if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
825 if_printf(sc->hn_ifp, "RNDIS set 0x%08x failed: "
826 "status 0x%08x\n", oid, comp->rm_status);
832 vmbus_xact_put(xact);
837 hn_rndis_conf_offload(struct hn_softc *sc, int mtu)
839 struct ndis_offload hwcaps;
840 struct ndis_offload_params params;
843 int error, tso_maxsz, tso_minsg;
845 error = hn_rndis_query_hwcaps(sc, &hwcaps);
847 if_printf(sc->hn_ifp, "hwcaps query failed: %d\n", error);
851 /* NOTE: 0 means "no change" */
852 memset(¶ms, 0, sizeof(params));
854 params.ndis_hdr.ndis_type = NDIS_OBJTYPE_DEFAULT;
855 if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30) {
856 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_2;
857 paramsz = NDIS_OFFLOAD_PARAMS_SIZE_6_1;
859 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3;
860 paramsz = NDIS_OFFLOAD_PARAMS_SIZE;
862 params.ndis_hdr.ndis_size = paramsz;
867 tso_maxsz = IP_MAXPACKET;
869 if (hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
871 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON;
873 if (hwcaps.ndis_lsov2.ndis_ip4_maxsz < tso_maxsz)
874 tso_maxsz = hwcaps.ndis_lsov2.ndis_ip4_maxsz;
875 if (hwcaps.ndis_lsov2.ndis_ip4_minsg > tso_minsg)
876 tso_minsg = hwcaps.ndis_lsov2.ndis_ip4_minsg;
878 if ((hwcaps.ndis_lsov2.ndis_ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
879 (hwcaps.ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6) ==
880 HN_NDIS_LSOV2_CAP_IP6) {
883 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON;
885 if (hwcaps.ndis_lsov2.ndis_ip6_maxsz < tso_maxsz)
886 tso_maxsz = hwcaps.ndis_lsov2.ndis_ip6_maxsz;
887 if (hwcaps.ndis_lsov2.ndis_ip6_minsg > tso_minsg)
888 tso_minsg = hwcaps.ndis_lsov2.ndis_ip6_minsg;
891 sc->hn_ndis_tso_szmax = 0;
892 sc->hn_ndis_tso_sgmin = 0;
893 if (caps & (HN_CAP_TSO4 | HN_CAP_TSO6)) {
894 KASSERT(tso_maxsz <= IP_MAXPACKET,
895 ("invalid NDIS TSO maxsz %d", tso_maxsz));
896 KASSERT(tso_minsg >= 2,
897 ("invalid NDIS TSO minsg %d", tso_minsg));
898 if (tso_maxsz < tso_minsg * mtu) {
899 if_printf(sc->hn_ifp, "invalid NDIS TSO config: "
900 "maxsz %d, minsg %d, mtu %d; "
901 "disable TSO4 and TSO6\n",
902 tso_maxsz, tso_minsg, mtu);
903 caps &= ~(HN_CAP_TSO4 | HN_CAP_TSO6);
904 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_OFF;
905 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_OFF;
907 sc->hn_ndis_tso_szmax = tso_maxsz;
908 sc->hn_ndis_tso_sgmin = tso_minsg;
910 if_printf(sc->hn_ifp, "NDIS TSO "
911 "szmax %d sgmin %d\n",
912 sc->hn_ndis_tso_szmax,
913 sc->hn_ndis_tso_sgmin);
919 if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4) ==
920 HN_NDIS_TXCSUM_CAP_IP4) {
922 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TX;
924 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4) {
925 if (params.ndis_ip4csum == NDIS_OFFLOAD_PARAM_TX)
926 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX;
928 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_RX;
932 if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_TCP4) ==
933 HN_NDIS_TXCSUM_CAP_TCP4) {
934 caps |= HN_CAP_TCP4CS;
935 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TX;
937 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) {
938 if (params.ndis_tcp4csum == NDIS_OFFLOAD_PARAM_TX)
939 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX;
941 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_RX;
945 if (hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
946 caps |= HN_CAP_UDP4CS;
947 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TX;
949 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) {
950 if (params.ndis_udp4csum == NDIS_OFFLOAD_PARAM_TX)
951 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX;
953 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_RX;
957 if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_TCP6) ==
958 HN_NDIS_TXCSUM_CAP_TCP6) {
959 caps |= HN_CAP_TCP6CS;
960 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TX;
962 if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6) {
963 if (params.ndis_tcp6csum == NDIS_OFFLOAD_PARAM_TX)
964 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX;
966 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_RX;
970 if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_UDP6) ==
971 HN_NDIS_TXCSUM_CAP_UDP6) {
972 caps |= HN_CAP_UDP6CS;
973 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TX;
975 if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6) {
976 if (params.ndis_udp6csum == NDIS_OFFLOAD_PARAM_TX)
977 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX;
979 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_RX;
983 if_printf(sc->hn_ifp, "offload csum: "
984 "ip4 %u, tcp4 %u, udp4 %u, tcp6 %u, udp6 %u\n",
986 params.ndis_tcp4csum,
987 params.ndis_udp4csum,
988 params.ndis_tcp6csum,
989 params.ndis_udp6csum);
990 if_printf(sc->hn_ifp, "offload lsov2: ip4 %u, ip6 %u\n",
991 params.ndis_lsov2_ip4,
992 params.ndis_lsov2_ip6);
995 error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, ¶ms, paramsz);
997 if_printf(sc->hn_ifp, "offload config failed: %d\n", error);
1002 if_printf(sc->hn_ifp, "offload config done\n");
1003 sc->hn_caps |= caps;
1008 hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags)
1010 struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
1011 struct ndis_rss_params *prm = &rss->rss_params;
1015 * Only NDIS 6.30+ is supported.
1017 KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30,
1018 ("NDIS 6.30+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
1022 * DO NOT whack rss_key and rss_ind, which are setup by the caller.
1024 memset(prm, 0, sizeof(*prm));
1026 prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS;
1027 prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2;
1028 prm->ndis_hdr.ndis_size = sizeof(*rss);
1029 prm->ndis_flags = flags;
1030 prm->ndis_hash = NDIS_HASH_FUNCTION_TOEPLITZ |
1031 NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4 |
1032 NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6;
1033 /* TODO: Take ndis_rss_caps.ndis_nind into account */
1034 prm->ndis_indsize = sizeof(rss->rss_ind);
1035 prm->ndis_indoffset =
1036 __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]);
1037 prm->ndis_keysize = sizeof(rss->rss_key);
1038 prm->ndis_keyoffset =
1039 __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]);
1041 error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS,
1044 if_printf(sc->hn_ifp, "RSS config failed: %d\n", error);
1047 if_printf(sc->hn_ifp, "RSS config done\n");
1053 hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter)
1057 error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER,
1058 &filter, sizeof(filter));
1060 if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n",
1064 if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n",
1072 hn_rndis_init(struct hn_softc *sc)
1074 struct rndis_init_req *req;
1075 const struct rndis_init_comp *comp;
1076 struct vmbus_xact *xact;
1081 xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
1083 if_printf(sc->hn_ifp, "no xact for RNDIS init\n");
1086 rid = hn_rndis_rid(sc);
1087 req = vmbus_xact_req_data(xact);
1088 req->rm_type = REMOTE_NDIS_INITIALIZE_MSG;
1089 req->rm_len = sizeof(*req);
1091 req->rm_ver_major = RNDIS_VERSION_MAJOR;
1092 req->rm_ver_minor = RNDIS_VERSION_MINOR;
1093 req->rm_max_xfersz = HN_RNDIS_XFER_SIZE;
1095 comp_len = RNDIS_INIT_COMP_SIZE_MIN;
1096 comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len,
1097 REMOTE_NDIS_INITIALIZE_CMPLT);
1099 if_printf(sc->hn_ifp, "exec RNDIS init failed\n");
1104 if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
1105 if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n",
1111 if_printf(sc->hn_ifp, "RNDIS ver %u.%u, pktsz %u, pktcnt %u, "
1112 "align %u\n", comp->rm_ver_major, comp->rm_ver_minor,
1113 comp->rm_pktmaxsz, comp->rm_pktmaxcnt,
1114 1U << comp->rm_align);
1118 vmbus_xact_put(xact);
1123 hn_rndis_halt(struct hn_softc *sc)
1125 struct vmbus_xact *xact;
1126 struct rndis_halt_req *halt;
1127 struct hn_send_ctx sndc;
1130 xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt));
1132 if_printf(sc->hn_ifp, "no xact for RNDIS halt\n");
1135 halt = vmbus_xact_req_data(xact);
1136 halt->rm_type = REMOTE_NDIS_HALT_MSG;
1137 halt->rm_len = sizeof(*halt);
1138 halt->rm_rid = hn_rndis_rid(sc);
1140 /* No RNDIS completion; rely on NVS message send completion */
1141 hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact);
1142 hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len);
1144 vmbus_xact_put(xact);
1146 if_printf(sc->hn_ifp, "RNDIS halt done\n");
1151 hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps)
1153 struct ndis_offload in;
1154 size_t caps_len, size;
1157 memset(&in, 0, sizeof(in));
1158 in.ndis_hdr.ndis_type = NDIS_OBJTYPE_OFFLOAD;
1159 if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) {
1160 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_3;
1161 size = NDIS_OFFLOAD_SIZE;
1162 } else if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_1) {
1163 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_2;
1164 size = NDIS_OFFLOAD_SIZE_2;
1166 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1;
1167 size = NDIS_OFFLOAD_SIZE_1;
1169 in.ndis_hdr.ndis_size = size;
1171 caps_len = NDIS_OFFLOAD_SIZE;
1172 error = hn_rndis_query2(sc, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
1173 &in, size, caps, &caps_len, NDIS_OFFLOAD_SIZE_1);
1178 * Preliminary verification.
1180 if (caps->ndis_hdr.ndis_type != NDIS_OBJTYPE_OFFLOAD) {
1181 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
1182 caps->ndis_hdr.ndis_type);
1185 if (caps->ndis_hdr.ndis_rev < NDIS_OFFLOAD_REV_1) {
1186 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
1187 caps->ndis_hdr.ndis_rev);
1190 if (caps->ndis_hdr.ndis_size > caps_len) {
1191 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
1192 "data size %zu\n", caps->ndis_hdr.ndis_size, caps_len);
1194 } else if (caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE_1) {
1195 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
1196 caps->ndis_hdr.ndis_size);
1202 * Fields for NDIS 6.0 are accessable.
1204 if_printf(sc->hn_ifp, "hwcaps rev %u\n",
1205 caps->ndis_hdr.ndis_rev);
1207 if_printf(sc->hn_ifp, "hwcaps csum: "
1208 "ip4 tx 0x%x/0x%x rx 0x%x/0x%x, "
1209 "ip6 tx 0x%x/0x%x rx 0x%x/0x%x\n",
1210 caps->ndis_csum.ndis_ip4_txcsum,
1211 caps->ndis_csum.ndis_ip4_txenc,
1212 caps->ndis_csum.ndis_ip4_rxcsum,
1213 caps->ndis_csum.ndis_ip4_rxenc,
1214 caps->ndis_csum.ndis_ip6_txcsum,
1215 caps->ndis_csum.ndis_ip6_txenc,
1216 caps->ndis_csum.ndis_ip6_rxcsum,
1217 caps->ndis_csum.ndis_ip6_rxenc);
1218 if_printf(sc->hn_ifp, "hwcaps lsov2: "
1219 "ip4 maxsz %u minsg %u encap 0x%x, "
1220 "ip6 maxsz %u minsg %u encap 0x%x opts 0x%x\n",
1221 caps->ndis_lsov2.ndis_ip4_maxsz,
1222 caps->ndis_lsov2.ndis_ip4_minsg,
1223 caps->ndis_lsov2.ndis_ip4_encap,
1224 caps->ndis_lsov2.ndis_ip6_maxsz,
1225 caps->ndis_lsov2.ndis_ip6_minsg,
1226 caps->ndis_lsov2.ndis_ip6_encap,
1227 caps->ndis_lsov2.ndis_ip6_opts);
1233 hn_rndis_attach(struct hn_softc *sc, int mtu)
1240 error = hn_rndis_init(sc);
1245 * Configure NDIS offload settings.
1246 * XXX no offloading, if error happened?
1248 hn_rndis_conf_offload(sc, mtu);
1253 hn_rndis_detach(struct hn_softc *sc)
1256 /* Halt the RNDIS. */
1261 hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
1264 hn_chan_rollup(rxr, txr);