]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/netvsc/hv_rndis_filter.c
MFC 307983
[FreeBSD/stable/10.git] / sys / dev / hyperv / netvsc / hv_rndis_filter.c
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$");
31
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/mbuf.h>
35 #include <sys/socket.h>
36 #include <sys/lock.h>
37 #include <sys/mutex.h>
38 #include <net/if.h>
39 #include <net/if_arp.h>
40 #include <net/if_var.h>
41 #include <net/ethernet.h>
42 #include <net/rndis.h>
43 #include <netinet/in.h>
44 #include <netinet/ip.h>
45 #include <sys/types.h>
46 #include <machine/atomic.h>
47 #include <sys/sema.h>
48 #include <vm/vm.h>
49 #include <vm/vm_param.h>
50 #include <vm/pmap.h>
51
52 #include <dev/hyperv/include/hyperv.h>
53 #include <dev/hyperv/include/vmbus_xact.h>
54 #include <dev/hyperv/netvsc/hv_net_vsc.h>
55 #include <dev/hyperv/netvsc/hv_rndis_filter.h>
56 #include <dev/hyperv/netvsc/if_hnreg.h>
57 #include <dev/hyperv/netvsc/ndis.h>
58
59 #define HV_RF_RECVINFO_VLAN     0x1
60 #define HV_RF_RECVINFO_CSUM     0x2
61 #define HV_RF_RECVINFO_HASHINF  0x4
62 #define HV_RF_RECVINFO_HASHVAL  0x8
63 #define HV_RF_RECVINFO_ALL              \
64         (HV_RF_RECVINFO_VLAN |          \
65          HV_RF_RECVINFO_CSUM |          \
66          HV_RF_RECVINFO_HASHINF |       \
67          HV_RF_RECVINFO_HASHVAL)
68
69 #define HN_RNDIS_RID_COMPAT_MASK        0xffff
70 #define HN_RNDIS_RID_COMPAT_MAX         HN_RNDIS_RID_COMPAT_MASK
71
72 #define HN_RNDIS_XFER_SIZE              2048
73
74 #define HN_NDIS_TXCSUM_CAP_IP4          \
75         (NDIS_TXCSUM_CAP_IP4 | NDIS_TXCSUM_CAP_IP4OPT)
76 #define HN_NDIS_TXCSUM_CAP_TCP4         \
77         (NDIS_TXCSUM_CAP_TCP4 | NDIS_TXCSUM_CAP_TCP4OPT)
78 #define HN_NDIS_TXCSUM_CAP_TCP6         \
79         (NDIS_TXCSUM_CAP_TCP6 | NDIS_TXCSUM_CAP_TCP6OPT | \
80          NDIS_TXCSUM_CAP_IP6EXT)
81 #define HN_NDIS_TXCSUM_CAP_UDP6         \
82         (NDIS_TXCSUM_CAP_UDP6 | NDIS_TXCSUM_CAP_IP6EXT)
83 #define HN_NDIS_LSOV2_CAP_IP6           \
84         (NDIS_LSOV2_CAP_IP6EXT | NDIS_LSOV2_CAP_TCP6OPT)
85
86 /*
87  * Forward declarations
88  */
89 static void hv_rf_receive_indicate_status(struct hn_softc *sc,
90     const void *data, int dlen);
91 static void hv_rf_receive_data(struct hn_rx_ring *rxr,
92     const void *data, int dlen);
93
94 static int hn_rndis_query(struct hn_softc *sc, uint32_t oid,
95     const void *idata, size_t idlen, void *odata, size_t *odlen0);
96 static int hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
97     const void *idata, size_t idlen, void *odata, size_t *odlen0,
98     size_t min_odlen);
99 static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data,
100     size_t dlen);
101 static int hn_rndis_conf_offload(struct hn_softc *sc, int mtu);
102 static int hn_rndis_query_hwcaps(struct hn_softc *sc,
103     struct ndis_offload *caps);
104
105 static __inline uint32_t
106 hn_rndis_rid(struct hn_softc *sc)
107 {
108         uint32_t rid;
109
110 again:
111         rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1);
112         if (rid == 0)
113                 goto again;
114
115         /* Use upper 16 bits for non-compat RNDIS messages. */
116         return ((rid & 0xffff) << 16);
117 }
118
119 void *
120 hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
121     size_t pi_dlen, uint32_t pi_type)
122 {
123         const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen);
124         struct rndis_pktinfo *pi;
125
126         KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0,
127             ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen));
128
129         /*
130          * Per-packet-info does not move; it only grows.
131          *
132          * NOTE:
133          * rm_pktinfooffset in this phase counts from the beginning
134          * of rndis_packet_msg.
135          */
136         KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize,
137             ("%u pktinfo overflows RNDIS packet msg", pi_type));
138         pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset +
139             pkt->rm_pktinfolen);
140         pkt->rm_pktinfolen += pi_size;
141
142         pi->rm_size = pi_size;
143         pi->rm_type = pi_type;
144         pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET;
145
146         /* Data immediately follow per-packet-info. */
147         pkt->rm_dataoffset += pi_size;
148
149         /* Update RNDIS packet msg length */
150         pkt->rm_len += pi_size;
151
152         return (pi->rm_data);
153 }
154
155 /*
156  * RNDIS filter receive indicate status
157  */
158 static void 
159 hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen)
160 {
161         const struct rndis_status_msg *msg;
162         int ofs;
163
164         if (dlen < sizeof(*msg)) {
165                 if_printf(sc->hn_ifp, "invalid RNDIS status\n");
166                 return;
167         }
168         msg = data;
169
170         switch (msg->rm_status) {
171         case RNDIS_STATUS_MEDIA_CONNECT:
172         case RNDIS_STATUS_MEDIA_DISCONNECT:
173                 hn_link_status_update(sc);
174                 break;
175
176         case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
177                 /* Not really useful; ignore. */
178                 break;
179
180         case RNDIS_STATUS_NETWORK_CHANGE:
181                 ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset);
182                 if (dlen < ofs + msg->rm_stbuflen ||
183                     msg->rm_stbuflen < sizeof(uint32_t)) {
184                         if_printf(sc->hn_ifp, "network changed\n");
185                 } else {
186                         uint32_t change;
187
188                         memcpy(&change, ((const uint8_t *)msg) + ofs,
189                             sizeof(change));
190                         if_printf(sc->hn_ifp, "network changed, change %u\n",
191                             change);
192                 }
193                 hn_network_change(sc);
194                 break;
195
196         default:
197                 /* TODO: */
198                 if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
199                     msg->rm_status);
200                 break;
201         }
202 }
203
204 static int
205 hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_recvinfo *info)
206 {
207         const struct rndis_pktinfo *pi = info_data;
208         uint32_t mask = 0;
209
210         while (info_dlen != 0) {
211                 const void *data;
212                 uint32_t dlen;
213
214                 if (__predict_false(info_dlen < sizeof(*pi)))
215                         return (EINVAL);
216                 if (__predict_false(info_dlen < pi->rm_size))
217                         return (EINVAL);
218                 info_dlen -= pi->rm_size;
219
220                 if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
221                         return (EINVAL);
222                 if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
223                         return (EINVAL);
224                 dlen = pi->rm_size - pi->rm_pktinfooffset;
225                 data = pi->rm_data;
226
227                 switch (pi->rm_type) {
228                 case NDIS_PKTINFO_TYPE_VLAN:
229                         if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE))
230                                 return (EINVAL);
231                         info->vlan_info = *((const uint32_t *)data);
232                         mask |= HV_RF_RECVINFO_VLAN;
233                         break;
234
235                 case NDIS_PKTINFO_TYPE_CSUM:
236                         if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE))
237                                 return (EINVAL);
238                         info->csum_info = *((const uint32_t *)data);
239                         mask |= HV_RF_RECVINFO_CSUM;
240                         break;
241
242                 case HN_NDIS_PKTINFO_TYPE_HASHVAL:
243                         if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE))
244                                 return (EINVAL);
245                         info->hash_value = *((const uint32_t *)data);
246                         mask |= HV_RF_RECVINFO_HASHVAL;
247                         break;
248
249                 case HN_NDIS_PKTINFO_TYPE_HASHINF:
250                         if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE))
251                                 return (EINVAL);
252                         info->hash_info = *((const uint32_t *)data);
253                         mask |= HV_RF_RECVINFO_HASHINF;
254                         break;
255
256                 default:
257                         goto next;
258                 }
259
260                 if (mask == HV_RF_RECVINFO_ALL) {
261                         /* All found; done */
262                         break;
263                 }
264 next:
265                 pi = (const struct rndis_pktinfo *)
266                     ((const uint8_t *)pi + pi->rm_size);
267         }
268
269         /*
270          * Final fixup.
271          * - If there is no hash value, invalidate the hash info.
272          */
273         if ((mask & HV_RF_RECVINFO_HASHVAL) == 0)
274                 info->hash_info = HN_NDIS_HASH_INFO_INVALID;
275         return (0);
276 }
277
278 static __inline bool
279 hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
280 {
281
282         if (off < check_off) {
283                 if (__predict_true(off + len <= check_off))
284                         return (false);
285         } else if (off > check_off) {
286                 if (__predict_true(check_off + check_len <= off))
287                         return (false);
288         }
289         return (true);
290 }
291
292 /*
293  * RNDIS filter receive data
294  */
295 static void
296 hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen)
297 {
298         const struct rndis_packet_msg *pkt;
299         struct hn_recvinfo info;
300         int data_off, pktinfo_off, data_len, pktinfo_len;
301
302         /*
303          * Check length.
304          */
305         if (__predict_false(dlen < sizeof(*pkt))) {
306                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
307                 return;
308         }
309         pkt = data;
310
311         if (__predict_false(dlen < pkt->rm_len)) {
312                 if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
313                     "dlen %d, msglen %u\n", dlen, pkt->rm_len);
314                 return;
315         }
316         if (__predict_false(pkt->rm_len <
317             pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
318                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
319                     "msglen %u, data %u, oob %u, pktinfo %u\n",
320                     pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
321                     pkt->rm_pktinfolen);
322                 return;
323         }
324         if (__predict_false(pkt->rm_datalen == 0)) {
325                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
326                 return;
327         }
328
329         /*
330          * Check offests.
331          */
332 #define IS_OFFSET_INVALID(ofs)                  \
333         ((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \
334          ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
335
336         /* XXX Hyper-V does not meet data offset alignment requirement */
337         if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
338                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
339                     "data offset %u\n", pkt->rm_dataoffset);
340                 return;
341         }
342         if (__predict_false(pkt->rm_oobdataoffset > 0 &&
343             IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
344                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
345                     "oob offset %u\n", pkt->rm_oobdataoffset);
346                 return;
347         }
348         if (__predict_true(pkt->rm_pktinfooffset > 0) &&
349             __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
350                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
351                     "pktinfo offset %u\n", pkt->rm_pktinfooffset);
352                 return;
353         }
354
355 #undef IS_OFFSET_INVALID
356
357         data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
358         data_len = pkt->rm_datalen;
359         pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
360         pktinfo_len = pkt->rm_pktinfolen;
361
362         /*
363          * Check OOB coverage.
364          */
365         if (__predict_false(pkt->rm_oobdatalen != 0)) {
366                 int oob_off, oob_len;
367
368                 if_printf(rxr->hn_ifp, "got oobdata\n");
369                 oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
370                 oob_len = pkt->rm_oobdatalen;
371
372                 if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
373                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
374                             "oob overflow, msglen %u, oob abs %d len %d\n",
375                             pkt->rm_len, oob_off, oob_len);
376                         return;
377                 }
378
379                 /*
380                  * Check against data.
381                  */
382                 if (hn_rndis_check_overlap(oob_off, oob_len,
383                     data_off, data_len)) {
384                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
385                             "oob overlaps data, oob abs %d len %d, "
386                             "data abs %d len %d\n",
387                             oob_off, oob_len, data_off, data_len);
388                         return;
389                 }
390
391                 /*
392                  * Check against pktinfo.
393                  */
394                 if (pktinfo_len != 0 &&
395                     hn_rndis_check_overlap(oob_off, oob_len,
396                     pktinfo_off, pktinfo_len)) {
397                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
398                             "oob overlaps pktinfo, oob abs %d len %d, "
399                             "pktinfo abs %d len %d\n",
400                             oob_off, oob_len, pktinfo_off, pktinfo_len);
401                         return;
402                 }
403         }
404
405         /*
406          * Check per-packet-info coverage and find useful per-packet-info.
407          */
408         info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
409         info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
410         info.hash_info = HN_NDIS_HASH_INFO_INVALID;
411         if (__predict_true(pktinfo_len != 0)) {
412                 bool overlap;
413                 int error;
414
415                 if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
416                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
417                             "pktinfo overflow, msglen %u, "
418                             "pktinfo abs %d len %d\n",
419                             pkt->rm_len, pktinfo_off, pktinfo_len);
420                         return;
421                 }
422
423                 /*
424                  * Check packet info coverage.
425                  */
426                 overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
427                     data_off, data_len);
428                 if (__predict_false(overlap)) {
429                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
430                             "pktinfo overlap data, pktinfo abs %d len %d, "
431                             "data abs %d len %d\n",
432                             pktinfo_off, pktinfo_len, data_off, data_len);
433                         return;
434                 }
435
436                 /*
437                  * Find useful per-packet-info.
438                  */
439                 error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
440                     pktinfo_len, &info);
441                 if (__predict_false(error)) {
442                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
443                             "pktinfo\n");
444                         return;
445                 }
446         }
447
448         if (__predict_false(data_off + data_len > pkt->rm_len)) {
449                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
450                     "data overflow, msglen %u, data abs %d len %d\n",
451                     pkt->rm_len, data_off, data_len);
452                 return;
453         }
454         hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
455 }
456
457 /*
458  * RNDIS filter on receive
459  */
460 void
461 hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
462     const void *data, int dlen)
463 {
464         const struct rndis_comp_hdr *comp;
465         const struct rndis_msghdr *hdr;
466
467         if (__predict_false(dlen < sizeof(*hdr))) {
468                 if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
469                 return;
470         }
471         hdr = data;
472
473         switch (hdr->rm_type) {
474         case REMOTE_NDIS_PACKET_MSG:
475                 hv_rf_receive_data(rxr, data, dlen);
476                 break;
477
478         case REMOTE_NDIS_INITIALIZE_CMPLT:
479         case REMOTE_NDIS_QUERY_CMPLT:
480         case REMOTE_NDIS_SET_CMPLT:
481         case REMOTE_NDIS_KEEPALIVE_CMPLT:       /* unused */
482                 if (dlen < sizeof(*comp)) {
483                         if_printf(rxr->hn_ifp, "invalid RNDIS cmplt\n");
484                         return;
485                 }
486                 comp = data;
487
488                 KASSERT(comp->rm_rid > HN_RNDIS_RID_COMPAT_MAX,
489                     ("invalid RNDIS rid 0x%08x\n", comp->rm_rid));
490                 vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen);
491                 break;
492
493         case REMOTE_NDIS_INDICATE_STATUS_MSG:
494                 hv_rf_receive_indicate_status(sc, data, dlen);
495                 break;
496
497         case REMOTE_NDIS_RESET_CMPLT:
498                 /*
499                  * Reset completed, no rid.
500                  *
501                  * NOTE:
502                  * RESET is not issued by hn(4), so this message should
503                  * _not_ be observed.
504                  */
505                 if_printf(rxr->hn_ifp, "RESET cmplt received\n");
506                 break;
507
508         default:
509                 if_printf(rxr->hn_ifp, "unknown RNDIS msg 0x%x\n",
510                     hdr->rm_type);
511                 break;
512         }
513 }
514
515 int
516 hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr)
517 {
518         size_t eaddr_len;
519         int error;
520
521         eaddr_len = ETHER_ADDR_LEN;
522         error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0,
523             eaddr, &eaddr_len);
524         if (error)
525                 return (error);
526         if (eaddr_len != ETHER_ADDR_LEN) {
527                 if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len);
528                 return (EINVAL);
529         }
530         return (0);
531 }
532
533 int
534 hn_rndis_get_linkstatus(struct hn_softc *sc, uint32_t *link_status)
535 {
536         size_t size;
537         int error;
538
539         size = sizeof(*link_status);
540         error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0,
541             link_status, &size);
542         if (error)
543                 return (error);
544         if (size != sizeof(uint32_t)) {
545                 if_printf(sc->hn_ifp, "invalid link status len %zu\n", size);
546                 return (EINVAL);
547         }
548         return (0);
549 }
550
551 static const void *
552 hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen,
553     struct hn_send_ctx *sndc, size_t *comp_len)
554 {
555         struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT];
556         int gpa_cnt, error;
557         bus_addr_t paddr;
558
559         KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0,
560             ("invalid request length %zu", reqlen));
561
562         /*
563          * Setup the SG list.
564          */
565         paddr = vmbus_xact_req_paddr(xact);
566         KASSERT((paddr & PAGE_MASK) == 0,
567             ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr));
568         for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) {
569                 int len = PAGE_SIZE;
570
571                 if (reqlen == 0)
572                         break;
573                 if (reqlen < len)
574                         len = reqlen;
575
576                 gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt;
577                 gpa[gpa_cnt].gpa_len = len;
578                 gpa[gpa_cnt].gpa_ofs = 0;
579
580                 reqlen -= len;
581         }
582         KASSERT(reqlen == 0, ("still have %zu request data left", reqlen));
583
584         /*
585          * Send this RNDIS control message and wait for its completion
586          * message.
587          */
588         vmbus_xact_activate(xact);
589         error = hn_nvs_send_rndis_ctrl(sc->hn_prichan, sndc, gpa, gpa_cnt);
590         if (error) {
591                 vmbus_xact_deactivate(xact);
592                 if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error);
593                 return (NULL);
594         }
595         return (vmbus_xact_wait(xact, comp_len));
596 }
597
598 static const void *
599 hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid,
600     size_t reqlen, size_t *comp_len0, uint32_t comp_type)
601 {
602         const struct rndis_comp_hdr *comp;
603         size_t comp_len, min_complen = *comp_len0;
604
605         KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid));
606         KASSERT(min_complen >= sizeof(*comp),
607             ("invalid minimum complete len %zu", min_complen));
608
609         /*
610          * Execute the xact setup by the caller.
611          */
612         comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_send_ctx_none,
613             &comp_len);
614         if (comp == NULL)
615                 return (NULL);
616
617         /*
618          * Check this RNDIS complete message.
619          */
620         if (comp_len < min_complen) {
621                 if (comp_len >= sizeof(*comp)) {
622                         /* rm_status field is valid */
623                         if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, "
624                             "status 0x%08x\n", comp_len, comp->rm_status);
625                 } else {
626                         if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n",
627                             comp_len);
628                 }
629                 return (NULL);
630         }
631         if (comp->rm_len < min_complen) {
632                 if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n",
633                     comp->rm_len);
634                 return (NULL);
635         }
636         if (comp->rm_type != comp_type) {
637                 if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, "
638                     "expect 0x%08x\n", comp->rm_type, comp_type);
639                 return (NULL);
640         }
641         if (comp->rm_rid != rid) {
642                 if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, "
643                     "expect %u\n", comp->rm_rid, rid);
644                 return (NULL);
645         }
646         /* All pass! */
647         *comp_len0 = comp_len;
648         return (comp);
649 }
650
651 static int
652 hn_rndis_query(struct hn_softc *sc, uint32_t oid,
653     const void *idata, size_t idlen, void *odata, size_t *odlen0)
654 {
655
656         return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0));
657 }
658
659 static int
660 hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
661     const void *idata, size_t idlen, void *odata, size_t *odlen0,
662     size_t min_odlen)
663 {
664         struct rndis_query_req *req;
665         const struct rndis_query_comp *comp;
666         struct vmbus_xact *xact;
667         size_t reqlen, odlen = *odlen0, comp_len;
668         int error, ofs;
669         uint32_t rid;
670
671         reqlen = sizeof(*req) + idlen;
672         xact = vmbus_xact_get(sc->hn_xact, reqlen);
673         if (xact == NULL) {
674                 if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid);
675                 return (ENXIO);
676         }
677         rid = hn_rndis_rid(sc);
678         req = vmbus_xact_req_data(xact);
679         req->rm_type = REMOTE_NDIS_QUERY_MSG;
680         req->rm_len = reqlen;
681         req->rm_rid = rid;
682         req->rm_oid = oid;
683         /*
684          * XXX
685          * This is _not_ RNDIS Spec conforming:
686          * "This MUST be set to 0 when there is no input data
687          *  associated with the OID."
688          *
689          * If this field was set to 0 according to the RNDIS Spec,
690          * Hyper-V would set non-SUCCESS status in the query
691          * completion.
692          */
693         req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET;
694
695         if (idlen > 0) {
696                 req->rm_infobuflen = idlen;
697                 /* Input data immediately follows RNDIS query. */
698                 memcpy(req + 1, idata, idlen);
699         }
700
701         comp_len = sizeof(*comp) + min_odlen;
702         comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
703             REMOTE_NDIS_QUERY_CMPLT);
704         if (comp == NULL) {
705                 if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid);
706                 error = EIO;
707                 goto done;
708         }
709
710         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
711                 if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: "
712                     "status 0x%08x\n", oid, comp->rm_status);
713                 error = EIO;
714                 goto done;
715         }
716         if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) {
717                 /* No output data! */
718                 if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid);
719                 *odlen0 = 0;
720                 error = 0;
721                 goto done;
722         }
723
724         /*
725          * Check output data length and offset.
726          */
727         /* ofs is the offset from the beginning of comp. */
728         ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset);
729         if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
730                 if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
731                     "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);
732                 error = EINVAL;
733                 goto done;
734         }
735
736         /*
737          * Save output data.
738          */
739         if (comp->rm_infobuflen < odlen)
740                 odlen = comp->rm_infobuflen;
741         memcpy(odata, ((const uint8_t *)comp) + ofs, odlen);
742         *odlen0 = odlen;
743
744         error = 0;
745 done:
746         vmbus_xact_put(xact);
747         return (error);
748 }
749
750 int
751 hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0)
752 {
753         struct ndis_rss_caps in, caps;
754         size_t caps_len;
755         int error, indsz, rxr_cnt, hash_fnidx;
756         uint32_t hash_func = 0, hash_types = 0;
757
758         *rxr_cnt0 = 0;
759
760         if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20)
761                 return (EOPNOTSUPP);
762
763         memset(&in, 0, sizeof(in));
764         in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS;
765         in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
766         in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
767
768         caps_len = NDIS_RSS_CAPS_SIZE;
769         error = hn_rndis_query2(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
770             &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len, NDIS_RSS_CAPS_SIZE_6_0);
771         if (error)
772                 return (error);
773
774         /*
775          * Preliminary verification.
776          */
777         if (caps.ndis_hdr.ndis_type != NDIS_OBJTYPE_RSS_CAPS) {
778                 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
779                     caps.ndis_hdr.ndis_type);
780                 return (EINVAL);
781         }
782         if (caps.ndis_hdr.ndis_rev < NDIS_RSS_CAPS_REV_1) {
783                 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
784                     caps.ndis_hdr.ndis_rev);
785                 return (EINVAL);
786         }
787         if (caps.ndis_hdr.ndis_size > caps_len) {
788                 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
789                     "data size %zu\n", caps.ndis_hdr.ndis_size, caps_len);
790                 return (EINVAL);
791         } else if (caps.ndis_hdr.ndis_size < NDIS_RSS_CAPS_SIZE_6_0) {
792                 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
793                     caps.ndis_hdr.ndis_size);
794                 return (EINVAL);
795         }
796
797         /*
798          * Save information for later RSS configuration.
799          */
800         if (caps.ndis_nrxr == 0) {
801                 if_printf(sc->hn_ifp, "0 RX rings!?\n");
802                 return (EINVAL);
803         }
804         if (bootverbose)
805                 if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr);
806         rxr_cnt = caps.ndis_nrxr;
807
808         if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE &&
809             caps.ndis_hdr.ndis_rev >= NDIS_RSS_CAPS_REV_2) {
810                 if (caps.ndis_nind > NDIS_HASH_INDCNT) {
811                         if_printf(sc->hn_ifp,
812                             "too many RSS indirect table entries %u\n",
813                             caps.ndis_nind);
814                         return (EOPNOTSUPP);
815                 }
816                 if (!powerof2(caps.ndis_nind)) {
817                         if_printf(sc->hn_ifp, "RSS indirect table size is not "
818                             "power-of-2 %u\n", caps.ndis_nind);
819                 }
820
821                 if (bootverbose) {
822                         if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
823                             caps.ndis_nind);
824                 }
825                 indsz = caps.ndis_nind;
826         } else {
827                 indsz = NDIS_HASH_INDCNT;
828         }
829         if (indsz < rxr_cnt) {
830                 if_printf(sc->hn_ifp, "# of RX rings (%d) > "
831                     "RSS indirect table size %d\n", rxr_cnt, indsz);
832                 rxr_cnt = indsz;
833         }
834
835         /*
836          * NOTE:
837          * Toeplitz is at the lowest bit, and it is prefered; so ffs(),
838          * instead of fls(), is used here.
839          */
840         hash_fnidx = ffs(caps.ndis_caps & NDIS_RSS_CAP_HASHFUNC_MASK);
841         if (hash_fnidx == 0) {
842                 if_printf(sc->hn_ifp, "no hash functions, caps 0x%08x\n",
843                     caps.ndis_caps);
844                 return (EOPNOTSUPP);
845         }
846         hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */
847
848         if (caps.ndis_caps & NDIS_RSS_CAP_IPV4)
849                 hash_types |= NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4;
850         if (caps.ndis_caps & NDIS_RSS_CAP_IPV6)
851                 hash_types |= NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6;
852         if (caps.ndis_caps & NDIS_RSS_CAP_IPV6_EX)
853                 hash_types |= NDIS_HASH_IPV6_EX | NDIS_HASH_TCP_IPV6_EX;
854         if (hash_types == 0) {
855                 if_printf(sc->hn_ifp, "no hash types, caps 0x%08x\n",
856                     caps.ndis_caps);
857                 return (EOPNOTSUPP);
858         }
859
860         /* Commit! */
861         sc->hn_rss_ind_size = indsz;
862         sc->hn_rss_hash = hash_func | hash_types;
863         *rxr_cnt0 = rxr_cnt;
864         return (0);
865 }
866
867 static int
868 hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen)
869 {
870         struct rndis_set_req *req;
871         const struct rndis_set_comp *comp;
872         struct vmbus_xact *xact;
873         size_t reqlen, comp_len;
874         uint32_t rid;
875         int error;
876
877         KASSERT(dlen > 0, ("invalid dlen %zu", dlen));
878
879         reqlen = sizeof(*req) + dlen;
880         xact = vmbus_xact_get(sc->hn_xact, reqlen);
881         if (xact == NULL) {
882                 if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid);
883                 return (ENXIO);
884         }
885         rid = hn_rndis_rid(sc);
886         req = vmbus_xact_req_data(xact);
887         req->rm_type = REMOTE_NDIS_SET_MSG;
888         req->rm_len = reqlen;
889         req->rm_rid = rid;
890         req->rm_oid = oid;
891         req->rm_infobuflen = dlen;
892         req->rm_infobufoffset = RNDIS_SET_REQ_INFOBUFOFFSET;
893         /* Data immediately follows RNDIS set. */
894         memcpy(req + 1, data, dlen);
895
896         comp_len = sizeof(*comp);
897         comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
898             REMOTE_NDIS_SET_CMPLT);
899         if (comp == NULL) {
900                 if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid);
901                 error = EIO;
902                 goto done;
903         }
904
905         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
906                 if_printf(sc->hn_ifp, "RNDIS set 0x%08x failed: "
907                     "status 0x%08x\n", oid, comp->rm_status);
908                 error = EIO;
909                 goto done;
910         }
911         error = 0;
912 done:
913         vmbus_xact_put(xact);
914         return (error);
915 }
916
917 static int
918 hn_rndis_conf_offload(struct hn_softc *sc, int mtu)
919 {
920         struct ndis_offload hwcaps;
921         struct ndis_offload_params params;
922         uint32_t caps = 0;
923         size_t paramsz;
924         int error, tso_maxsz, tso_minsg;
925
926         error = hn_rndis_query_hwcaps(sc, &hwcaps);
927         if (error) {
928                 if_printf(sc->hn_ifp, "hwcaps query failed: %d\n", error);
929                 return (error);
930         }
931
932         /* NOTE: 0 means "no change" */
933         memset(&params, 0, sizeof(params));
934
935         params.ndis_hdr.ndis_type = NDIS_OBJTYPE_DEFAULT;
936         if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30) {
937                 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_2;
938                 paramsz = NDIS_OFFLOAD_PARAMS_SIZE_6_1;
939         } else {
940                 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3;
941                 paramsz = NDIS_OFFLOAD_PARAMS_SIZE;
942         }
943         params.ndis_hdr.ndis_size = paramsz;
944
945         /*
946          * TSO4/TSO6 setup.
947          */
948         tso_maxsz = IP_MAXPACKET;
949         tso_minsg = 2;
950         if (hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
951                 caps |= HN_CAP_TSO4;
952                 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON;
953
954                 if (hwcaps.ndis_lsov2.ndis_ip4_maxsz < tso_maxsz)
955                         tso_maxsz = hwcaps.ndis_lsov2.ndis_ip4_maxsz;
956                 if (hwcaps.ndis_lsov2.ndis_ip4_minsg > tso_minsg)
957                         tso_minsg = hwcaps.ndis_lsov2.ndis_ip4_minsg;
958         }
959         if ((hwcaps.ndis_lsov2.ndis_ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
960             (hwcaps.ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6) ==
961             HN_NDIS_LSOV2_CAP_IP6) {
962 #ifdef notyet
963                 caps |= HN_CAP_TSO6;
964                 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON;
965
966                 if (hwcaps.ndis_lsov2.ndis_ip6_maxsz < tso_maxsz)
967                         tso_maxsz = hwcaps.ndis_lsov2.ndis_ip6_maxsz;
968                 if (hwcaps.ndis_lsov2.ndis_ip6_minsg > tso_minsg)
969                         tso_minsg = hwcaps.ndis_lsov2.ndis_ip6_minsg;
970 #endif
971         }
972         sc->hn_ndis_tso_szmax = 0;
973         sc->hn_ndis_tso_sgmin = 0;
974         if (caps & (HN_CAP_TSO4 | HN_CAP_TSO6)) {
975                 KASSERT(tso_maxsz <= IP_MAXPACKET,
976                     ("invalid NDIS TSO maxsz %d", tso_maxsz));
977                 KASSERT(tso_minsg >= 2,
978                     ("invalid NDIS TSO minsg %d", tso_minsg));
979                 if (tso_maxsz < tso_minsg * mtu) {
980                         if_printf(sc->hn_ifp, "invalid NDIS TSO config: "
981                             "maxsz %d, minsg %d, mtu %d; "
982                             "disable TSO4 and TSO6\n",
983                             tso_maxsz, tso_minsg, mtu);
984                         caps &= ~(HN_CAP_TSO4 | HN_CAP_TSO6);
985                         params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_OFF;
986                         params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_OFF;
987                 } else {
988                         sc->hn_ndis_tso_szmax = tso_maxsz;
989                         sc->hn_ndis_tso_sgmin = tso_minsg;
990                         if (bootverbose) {
991                                 if_printf(sc->hn_ifp, "NDIS TSO "
992                                     "szmax %d sgmin %d\n",
993                                     sc->hn_ndis_tso_szmax,
994                                     sc->hn_ndis_tso_sgmin);
995                         }
996                 }
997         }
998
999         /* IPv4 checksum */
1000         if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4) ==
1001             HN_NDIS_TXCSUM_CAP_IP4) {
1002                 caps |= HN_CAP_IPCS;
1003                 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TX;
1004         }
1005         if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4) {
1006                 if (params.ndis_ip4csum == NDIS_OFFLOAD_PARAM_TX)
1007                         params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX;
1008                 else
1009                         params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_RX;
1010         }
1011
1012         /* TCP4 checksum */
1013         if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_TCP4) ==
1014             HN_NDIS_TXCSUM_CAP_TCP4) {
1015                 caps |= HN_CAP_TCP4CS;
1016                 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TX;
1017         }
1018         if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) {
1019                 if (params.ndis_tcp4csum == NDIS_OFFLOAD_PARAM_TX)
1020                         params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX;
1021                 else
1022                         params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_RX;
1023         }
1024
1025         /* UDP4 checksum */
1026         if (hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
1027                 caps |= HN_CAP_UDP4CS;
1028                 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TX;
1029         }
1030         if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) {
1031                 if (params.ndis_udp4csum == NDIS_OFFLOAD_PARAM_TX)
1032                         params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX;
1033                 else
1034                         params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_RX;
1035         }
1036
1037         /* TCP6 checksum */
1038         if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_TCP6) ==
1039             HN_NDIS_TXCSUM_CAP_TCP6) {
1040                 caps |= HN_CAP_TCP6CS;
1041                 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TX;
1042         }
1043         if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6) {
1044                 if (params.ndis_tcp6csum == NDIS_OFFLOAD_PARAM_TX)
1045                         params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX;
1046                 else
1047                         params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_RX;
1048         }
1049
1050         /* UDP6 checksum */
1051         if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_UDP6) ==
1052             HN_NDIS_TXCSUM_CAP_UDP6) {
1053                 caps |= HN_CAP_UDP6CS;
1054                 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TX;
1055         }
1056         if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6) {
1057                 if (params.ndis_udp6csum == NDIS_OFFLOAD_PARAM_TX)
1058                         params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX;
1059                 else
1060                         params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_RX;
1061         }
1062
1063         if (bootverbose) {
1064                 if_printf(sc->hn_ifp, "offload csum: "
1065                     "ip4 %u, tcp4 %u, udp4 %u, tcp6 %u, udp6 %u\n",
1066                     params.ndis_ip4csum,
1067                     params.ndis_tcp4csum,
1068                     params.ndis_udp4csum,
1069                     params.ndis_tcp6csum,
1070                     params.ndis_udp6csum);
1071                 if_printf(sc->hn_ifp, "offload lsov2: ip4 %u, ip6 %u\n",
1072                     params.ndis_lsov2_ip4,
1073                     params.ndis_lsov2_ip6);
1074         }
1075
1076         error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, &params, paramsz);
1077         if (error) {
1078                 if_printf(sc->hn_ifp, "offload config failed: %d\n", error);
1079                 return (error);
1080         }
1081
1082         if (bootverbose)
1083                 if_printf(sc->hn_ifp, "offload config done\n");
1084         sc->hn_caps |= caps;
1085         return (0);
1086 }
1087
1088 int
1089 hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags)
1090 {
1091         struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
1092         struct ndis_rss_params *prm = &rss->rss_params;
1093         int error, rss_size;
1094
1095         /*
1096          * Only NDIS 6.20+ is supported:
1097          * We only support 4bytes element in indirect table, which has been
1098          * adopted since NDIS 6.20.
1099          */
1100         KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_20,
1101             ("NDIS 6.20+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
1102
1103         /* XXX only one can be specified through, popcnt? */
1104         KASSERT((sc->hn_rss_hash & NDIS_HASH_FUNCTION_MASK), ("no hash func"));
1105         KASSERT((sc->hn_rss_hash & NDIS_HASH_TYPE_MASK), ("no hash types"));
1106         KASSERT(sc->hn_rss_ind_size > 0, ("no indirect table size"));
1107
1108         if (bootverbose) {
1109                 if_printf(sc->hn_ifp, "RSS indirect table size %d, "
1110                     "hash 0x%08x\n", sc->hn_rss_ind_size, sc->hn_rss_hash);
1111         }
1112
1113         /*
1114          * NOTE:
1115          * DO NOT whack rss_key and rss_ind, which are setup by the caller.
1116          */
1117         memset(prm, 0, sizeof(*prm));
1118         rss_size = NDIS_RSSPRM_TOEPLITZ_SIZE(sc->hn_rss_ind_size);
1119
1120         prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS;
1121         prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2;
1122         prm->ndis_hdr.ndis_size = rss_size;
1123         prm->ndis_flags = flags;
1124         prm->ndis_hash = sc->hn_rss_hash;
1125         prm->ndis_indsize = sizeof(rss->rss_ind[0]) * sc->hn_rss_ind_size;
1126         prm->ndis_indoffset =
1127             __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]);
1128         prm->ndis_keysize = sizeof(rss->rss_key);
1129         prm->ndis_keyoffset =
1130             __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]);
1131
1132         error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS,
1133             rss, rss_size);
1134         if (error) {
1135                 if_printf(sc->hn_ifp, "RSS config failed: %d\n", error);
1136         } else {
1137                 if (bootverbose)
1138                         if_printf(sc->hn_ifp, "RSS config done\n");
1139         }
1140         return (error);
1141 }
1142
1143 int
1144 hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter)
1145 {
1146         int error;
1147
1148         error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER,
1149             &filter, sizeof(filter));
1150         if (error) {
1151                 if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n",
1152                     filter, error);
1153         } else {
1154                 if (bootverbose) {
1155                         if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n",
1156                             filter);
1157                 }
1158         }
1159         return (error);
1160 }
1161
1162 static int
1163 hn_rndis_init(struct hn_softc *sc)
1164 {
1165         struct rndis_init_req *req;
1166         const struct rndis_init_comp *comp;
1167         struct vmbus_xact *xact;
1168         size_t comp_len;
1169         uint32_t rid;
1170         int error;
1171
1172         xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
1173         if (xact == NULL) {
1174                 if_printf(sc->hn_ifp, "no xact for RNDIS init\n");
1175                 return (ENXIO);
1176         }
1177         rid = hn_rndis_rid(sc);
1178         req = vmbus_xact_req_data(xact);
1179         req->rm_type = REMOTE_NDIS_INITIALIZE_MSG;
1180         req->rm_len = sizeof(*req);
1181         req->rm_rid = rid;
1182         req->rm_ver_major = RNDIS_VERSION_MAJOR;
1183         req->rm_ver_minor = RNDIS_VERSION_MINOR;
1184         req->rm_max_xfersz = HN_RNDIS_XFER_SIZE;
1185
1186         comp_len = RNDIS_INIT_COMP_SIZE_MIN;
1187         comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len,
1188             REMOTE_NDIS_INITIALIZE_CMPLT);
1189         if (comp == NULL) {
1190                 if_printf(sc->hn_ifp, "exec RNDIS init failed\n");
1191                 error = EIO;
1192                 goto done;
1193         }
1194
1195         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
1196                 if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n",
1197                     comp->rm_status);
1198                 error = EIO;
1199                 goto done;
1200         }
1201         if (bootverbose) {
1202                 if_printf(sc->hn_ifp, "RNDIS ver %u.%u, pktsz %u, pktcnt %u, "
1203                     "align %u\n", comp->rm_ver_major, comp->rm_ver_minor,
1204                     comp->rm_pktmaxsz, comp->rm_pktmaxcnt,
1205                     1U << comp->rm_align);
1206         }
1207         error = 0;
1208 done:
1209         vmbus_xact_put(xact);
1210         return (error);
1211 }
1212
1213 static int
1214 hn_rndis_halt(struct hn_softc *sc)
1215 {
1216         struct vmbus_xact *xact;
1217         struct rndis_halt_req *halt;
1218         struct hn_send_ctx sndc;
1219         size_t comp_len;
1220
1221         xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt));
1222         if (xact == NULL) {
1223                 if_printf(sc->hn_ifp, "no xact for RNDIS halt\n");
1224                 return (ENXIO);
1225         }
1226         halt = vmbus_xact_req_data(xact);
1227         halt->rm_type = REMOTE_NDIS_HALT_MSG;
1228         halt->rm_len = sizeof(*halt);
1229         halt->rm_rid = hn_rndis_rid(sc);
1230
1231         /* No RNDIS completion; rely on NVS message send completion */
1232         hn_send_ctx_init(&sndc, hn_nvs_sent_xact, xact);
1233         hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len);
1234
1235         vmbus_xact_put(xact);
1236         if (bootverbose)
1237                 if_printf(sc->hn_ifp, "RNDIS halt done\n");
1238         return (0);
1239 }
1240
1241 static int
1242 hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps)
1243 {
1244         struct ndis_offload in;
1245         size_t caps_len, size;
1246         int error;
1247
1248         memset(&in, 0, sizeof(in));
1249         in.ndis_hdr.ndis_type = NDIS_OBJTYPE_OFFLOAD;
1250         if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) {
1251                 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_3;
1252                 size = NDIS_OFFLOAD_SIZE;
1253         } else if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_1) {
1254                 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_2;
1255                 size = NDIS_OFFLOAD_SIZE_6_1;
1256         } else {
1257                 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1;
1258                 size = NDIS_OFFLOAD_SIZE_6_0;
1259         }
1260         in.ndis_hdr.ndis_size = size;
1261
1262         caps_len = NDIS_OFFLOAD_SIZE;
1263         error = hn_rndis_query2(sc, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
1264             &in, size, caps, &caps_len, NDIS_OFFLOAD_SIZE_6_0);
1265         if (error)
1266                 return (error);
1267
1268         /*
1269          * Preliminary verification.
1270          */
1271         if (caps->ndis_hdr.ndis_type != NDIS_OBJTYPE_OFFLOAD) {
1272                 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
1273                     caps->ndis_hdr.ndis_type);
1274                 return (EINVAL);
1275         }
1276         if (caps->ndis_hdr.ndis_rev < NDIS_OFFLOAD_REV_1) {
1277                 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
1278                     caps->ndis_hdr.ndis_rev);
1279                 return (EINVAL);
1280         }
1281         if (caps->ndis_hdr.ndis_size > caps_len) {
1282                 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
1283                     "data size %zu\n", caps->ndis_hdr.ndis_size, caps_len);
1284                 return (EINVAL);
1285         } else if (caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE_6_0) {
1286                 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
1287                     caps->ndis_hdr.ndis_size);
1288                 return (EINVAL);
1289         }
1290
1291         if (bootverbose) {
1292                 /*
1293                  * NOTE:
1294                  * caps->ndis_hdr.ndis_size MUST be checked before accessing
1295                  * NDIS 6.1+ specific fields.
1296                  */
1297                 if_printf(sc->hn_ifp, "hwcaps rev %u\n",
1298                     caps->ndis_hdr.ndis_rev);
1299
1300                 if_printf(sc->hn_ifp, "hwcaps csum: "
1301                     "ip4 tx 0x%x/0x%x rx 0x%x/0x%x, "
1302                     "ip6 tx 0x%x/0x%x rx 0x%x/0x%x\n",
1303                     caps->ndis_csum.ndis_ip4_txcsum,
1304                     caps->ndis_csum.ndis_ip4_txenc,
1305                     caps->ndis_csum.ndis_ip4_rxcsum,
1306                     caps->ndis_csum.ndis_ip4_rxenc,
1307                     caps->ndis_csum.ndis_ip6_txcsum,
1308                     caps->ndis_csum.ndis_ip6_txenc,
1309                     caps->ndis_csum.ndis_ip6_rxcsum,
1310                     caps->ndis_csum.ndis_ip6_rxenc);
1311                 if_printf(sc->hn_ifp, "hwcaps lsov2: "
1312                     "ip4 maxsz %u minsg %u encap 0x%x, "
1313                     "ip6 maxsz %u minsg %u encap 0x%x opts 0x%x\n",
1314                     caps->ndis_lsov2.ndis_ip4_maxsz,
1315                     caps->ndis_lsov2.ndis_ip4_minsg,
1316                     caps->ndis_lsov2.ndis_ip4_encap,
1317                     caps->ndis_lsov2.ndis_ip6_maxsz,
1318                     caps->ndis_lsov2.ndis_ip6_minsg,
1319                     caps->ndis_lsov2.ndis_ip6_encap,
1320                     caps->ndis_lsov2.ndis_ip6_opts);
1321         }
1322         return (0);
1323 }
1324
1325 int
1326 hn_rndis_attach(struct hn_softc *sc, int mtu)
1327 {
1328         int error;
1329
1330         /*
1331          * Initialize RNDIS.
1332          */
1333         error = hn_rndis_init(sc);
1334         if (error)
1335                 return (error);
1336
1337         /*
1338          * Configure NDIS offload settings.
1339          * XXX no offloading, if error happened?
1340          */
1341         hn_rndis_conf_offload(sc, mtu);
1342         return (0);
1343 }
1344
1345 void
1346 hn_rndis_detach(struct hn_softc *sc)
1347 {
1348
1349         /* Halt the RNDIS. */
1350         hn_rndis_halt(sc);
1351 }
1352
1353 void
1354 hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
1355 {
1356
1357         hn_chan_rollup(rxr, txr);
1358 }