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