]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/hyperv/netvsc/hv_rndis_filter.c
hyperv/hn: Reorganize sub-channel allocation.
[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 <sys/types.h>
43 #include <machine/atomic.h>
44 #include <sys/sema.h>
45 #include <vm/vm.h>
46 #include <vm/vm_param.h>
47 #include <vm/pmap.h>
48
49 #include <dev/hyperv/include/hyperv.h>
50 #include <dev/hyperv/include/vmbus_xact.h>
51 #include <dev/hyperv/netvsc/hv_net_vsc.h>
52 #include <dev/hyperv/netvsc/hv_rndis_filter.h>
53 #include <dev/hyperv/netvsc/if_hnreg.h>
54 #include <dev/hyperv/netvsc/ndis.h>
55
56 #define HV_RF_RECVINFO_VLAN     0x1
57 #define HV_RF_RECVINFO_CSUM     0x2
58 #define HV_RF_RECVINFO_HASHINF  0x4
59 #define HV_RF_RECVINFO_HASHVAL  0x8
60 #define HV_RF_RECVINFO_ALL              \
61         (HV_RF_RECVINFO_VLAN |          \
62          HV_RF_RECVINFO_CSUM |          \
63          HV_RF_RECVINFO_HASHINF |       \
64          HV_RF_RECVINFO_HASHVAL)
65
66 #define HN_RNDIS_RID_COMPAT_MASK        0xffff
67 #define HN_RNDIS_RID_COMPAT_MAX         HN_RNDIS_RID_COMPAT_MASK
68
69 #define HN_RNDIS_XFER_SIZE              2048
70
71 /*
72  * Forward declarations
73  */
74 static void hv_rf_receive_indicate_status(struct hn_softc *sc,
75     const void *data, int dlen);
76 static void hv_rf_receive_data(struct hn_rx_ring *rxr,
77     const void *data, int dlen);
78 static int hv_rf_query_device_mac(struct hn_softc *sc, uint8_t *eaddr);
79 static int hv_rf_query_device_link_status(struct hn_softc *sc,
80     uint32_t *link_status);
81 static int  hv_rf_init_device(struct hn_softc *sc);
82
83 static int hn_rndis_query(struct hn_softc *sc, uint32_t oid,
84     const void *idata, size_t idlen, void *odata, size_t *odlen0);
85 static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data,
86     size_t dlen);
87 static int hn_rndis_conf_offload(struct hn_softc *sc);
88 static int hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt);
89 static int hn_rndis_conf_rss(struct hn_softc *sc, int nchan);
90
91 static __inline uint32_t
92 hn_rndis_rid(struct hn_softc *sc)
93 {
94         uint32_t rid;
95
96 again:
97         rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1);
98         if (rid == 0)
99                 goto again;
100
101         /* Use upper 16 bits for non-compat RNDIS messages. */
102         return ((rid & 0xffff) << 16);
103 }
104
105 void *
106 hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
107     size_t pi_dlen, uint32_t pi_type)
108 {
109         const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen);
110         struct rndis_pktinfo *pi;
111
112         KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0,
113             ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen));
114
115         /*
116          * Per-packet-info does not move; it only grows.
117          *
118          * NOTE:
119          * rm_pktinfooffset in this phase counts from the beginning
120          * of rndis_packet_msg.
121          */
122         KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize,
123             ("%u pktinfo overflows RNDIS packet msg", pi_type));
124         pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset +
125             pkt->rm_pktinfolen);
126         pkt->rm_pktinfolen += pi_size;
127
128         pi->rm_size = pi_size;
129         pi->rm_type = pi_type;
130         pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET;
131
132         /* Data immediately follow per-packet-info. */
133         pkt->rm_dataoffset += pi_size;
134
135         /* Update RNDIS packet msg length */
136         pkt->rm_len += pi_size;
137
138         return (pi->rm_data);
139 }
140
141 /*
142  * RNDIS filter receive indicate status
143  */
144 static void 
145 hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen)
146 {
147         const struct rndis_status_msg *msg;
148
149         if (dlen < sizeof(*msg)) {
150                 if_printf(sc->hn_ifp, "invalid RNDIS status\n");
151                 return;
152         }
153         msg = data;
154
155         switch (msg->rm_status) {
156         case RNDIS_STATUS_MEDIA_CONNECT:
157                 netvsc_linkstatus_callback(sc, 1);
158                 break;
159
160         case RNDIS_STATUS_MEDIA_DISCONNECT:
161                 netvsc_linkstatus_callback(sc, 0);
162                 break;
163
164         default:
165                 /* TODO: */
166                 if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
167                     msg->rm_status);
168                 break;
169         }
170 }
171
172 static int
173 hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_recvinfo *info)
174 {
175         const struct rndis_pktinfo *pi = info_data;
176         uint32_t mask = 0;
177
178         while (info_dlen != 0) {
179                 const void *data;
180                 uint32_t dlen;
181
182                 if (__predict_false(info_dlen < sizeof(*pi)))
183                         return (EINVAL);
184                 if (__predict_false(info_dlen < pi->rm_size))
185                         return (EINVAL);
186                 info_dlen -= pi->rm_size;
187
188                 if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
189                         return (EINVAL);
190                 if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
191                         return (EINVAL);
192                 dlen = pi->rm_size - pi->rm_pktinfooffset;
193                 data = pi->rm_data;
194
195                 switch (pi->rm_type) {
196                 case NDIS_PKTINFO_TYPE_VLAN:
197                         if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE))
198                                 return (EINVAL);
199                         info->vlan_info = *((const uint32_t *)data);
200                         mask |= HV_RF_RECVINFO_VLAN;
201                         break;
202
203                 case NDIS_PKTINFO_TYPE_CSUM:
204                         if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE))
205                                 return (EINVAL);
206                         info->csum_info = *((const uint32_t *)data);
207                         mask |= HV_RF_RECVINFO_CSUM;
208                         break;
209
210                 case HN_NDIS_PKTINFO_TYPE_HASHVAL:
211                         if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE))
212                                 return (EINVAL);
213                         info->hash_value = *((const uint32_t *)data);
214                         mask |= HV_RF_RECVINFO_HASHVAL;
215                         break;
216
217                 case HN_NDIS_PKTINFO_TYPE_HASHINF:
218                         if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE))
219                                 return (EINVAL);
220                         info->hash_info = *((const uint32_t *)data);
221                         mask |= HV_RF_RECVINFO_HASHINF;
222                         break;
223
224                 default:
225                         goto next;
226                 }
227
228                 if (mask == HV_RF_RECVINFO_ALL) {
229                         /* All found; done */
230                         break;
231                 }
232 next:
233                 pi = (const struct rndis_pktinfo *)
234                     ((const uint8_t *)pi + pi->rm_size);
235         }
236
237         /*
238          * Final fixup.
239          * - If there is no hash value, invalidate the hash info.
240          */
241         if ((mask & HV_RF_RECVINFO_HASHVAL) == 0)
242                 info->hash_info = HN_NDIS_HASH_INFO_INVALID;
243         return (0);
244 }
245
246 static __inline bool
247 hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
248 {
249
250         if (off < check_off) {
251                 if (__predict_true(off + len <= check_off))
252                         return (false);
253         } else if (off > check_off) {
254                 if (__predict_true(check_off + check_len <= off))
255                         return (false);
256         }
257         return (true);
258 }
259
260 /*
261  * RNDIS filter receive data
262  */
263 static void
264 hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen)
265 {
266         const struct rndis_packet_msg *pkt;
267         struct hn_recvinfo info;
268         int data_off, pktinfo_off, data_len, pktinfo_len;
269
270         /*
271          * Check length.
272          */
273         if (__predict_false(dlen < sizeof(*pkt))) {
274                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
275                 return;
276         }
277         pkt = data;
278
279         if (__predict_false(dlen < pkt->rm_len)) {
280                 if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
281                     "dlen %d, msglen %u\n", dlen, pkt->rm_len);
282                 return;
283         }
284         if (__predict_false(pkt->rm_len <
285             pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
286                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
287                     "msglen %u, data %u, oob %u, pktinfo %u\n",
288                     pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
289                     pkt->rm_pktinfolen);
290                 return;
291         }
292         if (__predict_false(pkt->rm_datalen == 0)) {
293                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
294                 return;
295         }
296
297         /*
298          * Check offests.
299          */
300 #define IS_OFFSET_INVALID(ofs)                  \
301         ((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \
302          ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
303
304         /* XXX Hyper-V does not meet data offset alignment requirement */
305         if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
306                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
307                     "data offset %u\n", pkt->rm_dataoffset);
308                 return;
309         }
310         if (__predict_false(pkt->rm_oobdataoffset > 0 &&
311             IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
312                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
313                     "oob offset %u\n", pkt->rm_oobdataoffset);
314                 return;
315         }
316         if (__predict_true(pkt->rm_pktinfooffset > 0) &&
317             __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
318                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
319                     "pktinfo offset %u\n", pkt->rm_pktinfooffset);
320                 return;
321         }
322
323 #undef IS_OFFSET_INVALID
324
325         data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
326         data_len = pkt->rm_datalen;
327         pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
328         pktinfo_len = pkt->rm_pktinfolen;
329
330         /*
331          * Check OOB coverage.
332          */
333         if (__predict_false(pkt->rm_oobdatalen != 0)) {
334                 int oob_off, oob_len;
335
336                 if_printf(rxr->hn_ifp, "got oobdata\n");
337                 oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
338                 oob_len = pkt->rm_oobdatalen;
339
340                 if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
341                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
342                             "oob overflow, msglen %u, oob abs %d len %d\n",
343                             pkt->rm_len, oob_off, oob_len);
344                         return;
345                 }
346
347                 /*
348                  * Check against data.
349                  */
350                 if (hn_rndis_check_overlap(oob_off, oob_len,
351                     data_off, data_len)) {
352                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
353                             "oob overlaps data, oob abs %d len %d, "
354                             "data abs %d len %d\n",
355                             oob_off, oob_len, data_off, data_len);
356                         return;
357                 }
358
359                 /*
360                  * Check against pktinfo.
361                  */
362                 if (pktinfo_len != 0 &&
363                     hn_rndis_check_overlap(oob_off, oob_len,
364                     pktinfo_off, pktinfo_len)) {
365                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
366                             "oob overlaps pktinfo, oob abs %d len %d, "
367                             "pktinfo abs %d len %d\n",
368                             oob_off, oob_len, pktinfo_off, pktinfo_len);
369                         return;
370                 }
371         }
372
373         /*
374          * Check per-packet-info coverage and find useful per-packet-info.
375          */
376         info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
377         info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
378         info.hash_info = HN_NDIS_HASH_INFO_INVALID;
379         if (__predict_true(pktinfo_len != 0)) {
380                 bool overlap;
381                 int error;
382
383                 if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
384                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
385                             "pktinfo overflow, msglen %u, "
386                             "pktinfo abs %d len %d\n",
387                             pkt->rm_len, pktinfo_off, pktinfo_len);
388                         return;
389                 }
390
391                 /*
392                  * Check packet info coverage.
393                  */
394                 overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
395                     data_off, data_len);
396                 if (__predict_false(overlap)) {
397                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
398                             "pktinfo overlap data, pktinfo abs %d len %d, "
399                             "data abs %d len %d\n",
400                             pktinfo_off, pktinfo_len, data_off, data_len);
401                         return;
402                 }
403
404                 /*
405                  * Find useful per-packet-info.
406                  */
407                 error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
408                     pktinfo_len, &info);
409                 if (__predict_false(error)) {
410                         if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
411                             "pktinfo\n");
412                         return;
413                 }
414         }
415
416         if (__predict_false(data_off + data_len > pkt->rm_len)) {
417                 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
418                     "data overflow, msglen %u, data abs %d len %d\n",
419                     pkt->rm_len, data_off, data_len);
420                 return;
421         }
422         hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
423 }
424
425 /*
426  * RNDIS filter on receive
427  */
428 void
429 hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
430     const void *data, int dlen)
431 {
432         const struct rndis_comp_hdr *comp;
433         const struct rndis_msghdr *hdr;
434
435         if (__predict_false(dlen < sizeof(*hdr))) {
436                 if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
437                 return;
438         }
439         hdr = data;
440
441         switch (hdr->rm_type) {
442         case REMOTE_NDIS_PACKET_MSG:
443                 hv_rf_receive_data(rxr, data, dlen);
444                 break;
445
446         case REMOTE_NDIS_INITIALIZE_CMPLT:
447         case REMOTE_NDIS_QUERY_CMPLT:
448         case REMOTE_NDIS_SET_CMPLT:
449         case REMOTE_NDIS_KEEPALIVE_CMPLT:       /* unused */
450                 if (dlen < sizeof(*comp)) {
451                         if_printf(rxr->hn_ifp, "invalid RNDIS cmplt\n");
452                         return;
453                 }
454                 comp = data;
455
456                 KASSERT(comp->rm_rid > HN_RNDIS_RID_COMPAT_MAX,
457                     ("invalid RNDIS rid 0x%08x\n", comp->rm_rid));
458                 vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen);
459                 break;
460
461         case REMOTE_NDIS_INDICATE_STATUS_MSG:
462                 hv_rf_receive_indicate_status(sc, data, dlen);
463                 break;
464
465         case REMOTE_NDIS_RESET_CMPLT:
466                 /*
467                  * Reset completed, no rid.
468                  *
469                  * NOTE:
470                  * RESET is not issued by hn(4), so this message should
471                  * _not_ be observed.
472                  */
473                 if_printf(rxr->hn_ifp, "RESET cmplt received\n");
474                 break;
475
476         default:
477                 if_printf(rxr->hn_ifp, "unknown RNDIS msg 0x%x\n",
478                     hdr->rm_type);
479                 break;
480         }
481 }
482
483 /*
484  * RNDIS filter query device MAC address
485  */
486 static int
487 hv_rf_query_device_mac(struct hn_softc *sc, uint8_t *eaddr)
488 {
489         size_t eaddr_len;
490         int error;
491
492         eaddr_len = ETHER_ADDR_LEN;
493         error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0,
494             eaddr, &eaddr_len);
495         if (error)
496                 return (error);
497         if (eaddr_len != ETHER_ADDR_LEN) {
498                 if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len);
499                 return (EINVAL);
500         }
501         return (0);
502 }
503
504 /*
505  * RNDIS filter query device link status
506  */
507 static int
508 hv_rf_query_device_link_status(struct hn_softc *sc, uint32_t *link_status)
509 {
510         size_t size;
511         int error;
512
513         size = sizeof(*link_status);
514         error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0,
515             link_status, &size);
516         if (error)
517                 return (error);
518         if (size != sizeof(uint32_t)) {
519                 if_printf(sc->hn_ifp, "invalid link status len %zu\n", size);
520                 return (EINVAL);
521         }
522         return (0);
523 }
524
525 static uint8_t netvsc_hash_key[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
526         0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
527         0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
528         0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
529         0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
530         0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
531 };
532
533 static const void *
534 hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen,
535     struct hn_send_ctx *sndc, size_t *comp_len)
536 {
537         struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT];
538         int gpa_cnt, error;
539         bus_addr_t paddr;
540
541         KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0,
542             ("invalid request length %zu", reqlen));
543
544         /*
545          * Setup the SG list.
546          */
547         paddr = vmbus_xact_req_paddr(xact);
548         KASSERT((paddr & PAGE_MASK) == 0,
549             ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr));
550         for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) {
551                 int len = PAGE_SIZE;
552
553                 if (reqlen == 0)
554                         break;
555                 if (reqlen < len)
556                         len = reqlen;
557
558                 gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt;
559                 gpa[gpa_cnt].gpa_len = len;
560                 gpa[gpa_cnt].gpa_ofs = 0;
561
562                 reqlen -= len;
563         }
564         KASSERT(reqlen == 0, ("still have %zu request data left", reqlen));
565
566         /*
567          * Send this RNDIS control message and wait for its completion
568          * message.
569          */
570         vmbus_xact_activate(xact);
571         error = hv_nv_on_send(sc->hn_prichan, HN_NVS_RNDIS_MTYPE_CTRL, sndc,
572             gpa, gpa_cnt);
573         if (error) {
574                 vmbus_xact_deactivate(xact);
575                 if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error);
576                 return (NULL);
577         }
578         return (vmbus_xact_wait(xact, comp_len));
579 }
580
581 static const void *
582 hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid,
583     size_t reqlen, size_t *comp_len0, uint32_t comp_type)
584 {
585         const struct rndis_comp_hdr *comp;
586         size_t comp_len, min_complen = *comp_len0;
587
588         KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid));
589         KASSERT(min_complen >= sizeof(*comp),
590             ("invalid minimum complete len %zu", min_complen));
591
592         /*
593          * Execute the xact setup by the caller.
594          */
595         comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_send_ctx_none,
596             &comp_len);
597         if (comp == NULL)
598                 return (NULL);
599
600         /*
601          * Check this RNDIS complete message.
602          */
603         if (comp_len < min_complen) {
604                 if (comp_len >= sizeof(*comp)) {
605                         /* rm_status field is valid */
606                         if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, "
607                             "status 0x%08x\n", comp_len, comp->rm_status);
608                 } else {
609                         if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n",
610                             comp_len);
611                 }
612                 return (NULL);
613         }
614         if (comp->rm_len < min_complen) {
615                 if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n",
616                     comp->rm_len);
617                 return (NULL);
618         }
619         if (comp->rm_type != comp_type) {
620                 if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, "
621                     "expect 0x%08x\n", comp->rm_type, comp_type);
622                 return (NULL);
623         }
624         if (comp->rm_rid != rid) {
625                 if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, "
626                     "expect %u\n", comp->rm_rid, rid);
627                 return (NULL);
628         }
629         /* All pass! */
630         *comp_len0 = comp_len;
631         return (comp);
632 }
633
634 static int
635 hn_rndis_query(struct hn_softc *sc, uint32_t oid,
636     const void *idata, size_t idlen, void *odata, size_t *odlen0)
637 {
638         struct rndis_query_req *req;
639         const struct rndis_query_comp *comp;
640         struct vmbus_xact *xact;
641         size_t reqlen, odlen = *odlen0, comp_len;
642         int error, ofs;
643         uint32_t rid;
644
645         reqlen = sizeof(*req) + idlen;
646         xact = vmbus_xact_get(sc->hn_xact, reqlen);
647         if (xact == NULL) {
648                 if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid);
649                 return (ENXIO);
650         }
651         rid = hn_rndis_rid(sc);
652         req = vmbus_xact_req_data(xact);
653         req->rm_type = REMOTE_NDIS_QUERY_MSG;
654         req->rm_len = reqlen;
655         req->rm_rid = rid;
656         req->rm_oid = oid;
657         /*
658          * XXX
659          * This is _not_ RNDIS Spec conforming:
660          * "This MUST be set to 0 when there is no input data
661          *  associated with the OID."
662          *
663          * If this field was set to 0 according to the RNDIS Spec,
664          * Hyper-V would set non-SUCCESS status in the query
665          * completion.
666          */
667         req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET;
668
669         if (idlen > 0) {
670                 req->rm_infobuflen = idlen;
671                 /* Input data immediately follows RNDIS query. */
672                 memcpy(req + 1, idata, idlen);
673         }
674
675         comp_len = sizeof(*comp) + odlen;
676         comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
677             REMOTE_NDIS_QUERY_CMPLT);
678         if (comp == NULL) {
679                 if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid);
680                 error = EIO;
681                 goto done;
682         }
683
684         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
685                 if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: "
686                     "status 0x%08x\n", oid, comp->rm_status);
687                 error = EIO;
688                 goto done;
689         }
690         if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) {
691                 /* No output data! */
692                 if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid);
693                 *odlen0 = 0;
694                 error = 0;
695                 goto done;
696         }
697
698         /*
699          * Check output data length and offset.
700          */
701         /* ofs is the offset from the beginning of comp. */
702         ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset);
703         if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
704                 if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
705                     "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);
706                 error = EINVAL;
707                 goto done;
708         }
709
710         /*
711          * Save output data.
712          */
713         if (comp->rm_infobuflen < odlen)
714                 odlen = comp->rm_infobuflen;
715         memcpy(odata, ((const uint8_t *)comp) + ofs, odlen);
716         *odlen0 = odlen;
717
718         error = 0;
719 done:
720         vmbus_xact_put(xact);
721         return (error);
722 }
723
724 static int
725 hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt)
726 {
727         struct ndis_rss_caps in, caps;
728         size_t caps_len;
729         int error;
730
731         /*
732          * Only NDIS 6.30+ is supported.
733          */
734         KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30,
735             ("NDIS 6.30+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
736         *rxr_cnt = 0;
737
738         memset(&in, 0, sizeof(in));
739         in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS;
740         in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
741         in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
742
743         caps_len = NDIS_RSS_CAPS_SIZE;
744         error = hn_rndis_query(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
745             &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len);
746         if (error)
747                 return (error);
748         if (caps_len < NDIS_RSS_CAPS_SIZE_6_0) {
749                 if_printf(sc->hn_ifp, "invalid NDIS RSS caps len %zu",
750                     caps_len);
751                 return (EINVAL);
752         }
753
754         if (caps.ndis_nrxr == 0) {
755                 if_printf(sc->hn_ifp, "0 RX rings!?\n");
756                 return (EINVAL);
757         }
758         *rxr_cnt = caps.ndis_nrxr;
759
760         if (caps_len == NDIS_RSS_CAPS_SIZE) {
761                 if (bootverbose) {
762                         if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
763                             caps.ndis_nind);
764                 }
765         }
766         return (0);
767 }
768
769 static int
770 hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen)
771 {
772         struct rndis_set_req *req;
773         const struct rndis_set_comp *comp;
774         struct vmbus_xact *xact;
775         size_t reqlen, comp_len;
776         uint32_t rid;
777         int error;
778
779         KASSERT(dlen > 0, ("invalid dlen %zu", dlen));
780
781         reqlen = sizeof(*req) + dlen;
782         xact = vmbus_xact_get(sc->hn_xact, reqlen);
783         if (xact == NULL) {
784                 if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid);
785                 return (ENXIO);
786         }
787         rid = hn_rndis_rid(sc);
788         req = vmbus_xact_req_data(xact);
789         req->rm_type = REMOTE_NDIS_SET_MSG;
790         req->rm_len = reqlen;
791         req->rm_rid = rid;
792         req->rm_oid = oid;
793         req->rm_infobuflen = dlen;
794         req->rm_infobufoffset = RNDIS_SET_REQ_INFOBUFOFFSET;
795         /* Data immediately follows RNDIS set. */
796         memcpy(req + 1, data, dlen);
797
798         comp_len = sizeof(*comp);
799         comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
800             REMOTE_NDIS_SET_CMPLT);
801         if (comp == NULL) {
802                 if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid);
803                 error = EIO;
804                 goto done;
805         }
806
807         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
808                 if_printf(sc->hn_ifp, "RNDIS set 0x%08x failed: "
809                     "status 0x%08x\n", oid, comp->rm_status);
810                 error = EIO;
811                 goto done;
812         }
813         error = 0;
814 done:
815         vmbus_xact_put(xact);
816         return (error);
817 }
818
819 static int
820 hn_rndis_conf_offload(struct hn_softc *sc)
821 {
822         struct ndis_offload_params params;
823         size_t paramsz;
824         int error;
825
826         /* NOTE: 0 means "no change" */
827         memset(&params, 0, sizeof(params));
828
829         params.ndis_hdr.ndis_type = NDIS_OBJTYPE_DEFAULT;
830         if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30) {
831                 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_2;
832                 paramsz = NDIS_OFFLOAD_PARAMS_SIZE_6_1;
833         } else {
834                 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3;
835                 paramsz = NDIS_OFFLOAD_PARAMS_SIZE;
836         }
837         params.ndis_hdr.ndis_size = paramsz;
838
839         params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX;
840         params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX;
841         params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX;
842         if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) {
843                 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX;
844                 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX;
845         }
846         params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON;
847         /* XXX ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON */
848
849         error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, &params, paramsz);
850         if (error) {
851                 if_printf(sc->hn_ifp, "offload config failed: %d\n", error);
852         } else {
853                 if (bootverbose)
854                         if_printf(sc->hn_ifp, "offload config done\n");
855         }
856         return (error);
857 }
858
859 static int
860 hn_rndis_conf_rss(struct hn_softc *sc, int nchan)
861 {
862         struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
863         struct ndis_rss_params *prm = &rss->rss_params;
864         int i, error;
865
866         /*
867          * Only NDIS 6.30+ is supported.
868          */
869         KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30,
870             ("NDIS 6.30+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
871
872         memset(rss, 0, sizeof(*rss));
873         prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS;
874         prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2;
875         prm->ndis_hdr.ndis_size = sizeof(*rss);
876         prm->ndis_hash = NDIS_HASH_FUNCTION_TOEPLITZ |
877             NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4 |
878             NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6;
879         /* TODO: Take ndis_rss_caps.ndis_nind into account */
880         prm->ndis_indsize = sizeof(rss->rss_ind);
881         prm->ndis_indoffset =
882             __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]);
883         prm->ndis_keysize = sizeof(rss->rss_key);
884         prm->ndis_keyoffset =
885             __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]);
886
887         /* Setup RSS key */
888         memcpy(rss->rss_key, netvsc_hash_key, sizeof(rss->rss_key));
889
890         /* Setup RSS indirect table */
891         /* TODO: Take ndis_rss_caps.ndis_nind into account */
892         for (i = 0; i < NDIS_HASH_INDCNT; ++i)
893                 rss->rss_ind[i] = i % nchan;
894
895         error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS,
896             rss, sizeof(*rss));
897         if (error) {
898                 if_printf(sc->hn_ifp, "RSS config failed: %d\n", error);
899         } else {
900                 if (bootverbose)
901                         if_printf(sc->hn_ifp, "RSS config done\n");
902         }
903         return (error);
904 }
905
906 static int
907 hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter)
908 {
909         int error;
910
911         error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER,
912             &filter, sizeof(filter));
913         if (error) {
914                 if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n",
915                     filter, error);
916         } else {
917                 if (bootverbose) {
918                         if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n",
919                             filter);
920                 }
921         }
922         return (error);
923 }
924
925 /*
926  * RNDIS filter init device
927  */
928 static int
929 hv_rf_init_device(struct hn_softc *sc)
930 {
931         struct rndis_init_req *req;
932         const struct rndis_init_comp *comp;
933         struct vmbus_xact *xact;
934         size_t comp_len;
935         uint32_t rid;
936         int error;
937
938         xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
939         if (xact == NULL) {
940                 if_printf(sc->hn_ifp, "no xact for RNDIS init\n");
941                 return (ENXIO);
942         }
943         rid = hn_rndis_rid(sc);
944         req = vmbus_xact_req_data(xact);
945         req->rm_type = REMOTE_NDIS_INITIALIZE_MSG;
946         req->rm_len = sizeof(*req);
947         req->rm_rid = rid;
948         req->rm_ver_major = RNDIS_VERSION_MAJOR;
949         req->rm_ver_minor = RNDIS_VERSION_MINOR;
950         req->rm_max_xfersz = HN_RNDIS_XFER_SIZE;
951
952         comp_len = RNDIS_INIT_COMP_SIZE_MIN;
953         comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len,
954             REMOTE_NDIS_INITIALIZE_CMPLT);
955         if (comp == NULL) {
956                 if_printf(sc->hn_ifp, "exec RNDIS init failed\n");
957                 error = EIO;
958                 goto done;
959         }
960
961         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
962                 if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n",
963                     comp->rm_status);
964                 error = EIO;
965                 goto done;
966         }
967         if (bootverbose) {
968                 if_printf(sc->hn_ifp, "RNDIS ver %u.%u, pktsz %u, pktcnt %u, "
969                     "align %u\n", comp->rm_ver_major, comp->rm_ver_minor,
970                     comp->rm_pktmaxsz, comp->rm_pktmaxcnt,
971                     1U << comp->rm_align);
972         }
973         error = 0;
974 done:
975         vmbus_xact_put(xact);
976         return (error);
977 }
978
979 /*
980  * RNDIS filter halt device
981  */
982 static int
983 hv_rf_halt_device(struct hn_softc *sc)
984 {
985         struct vmbus_xact *xact;
986         struct rndis_halt_req *halt;
987         struct hn_send_ctx sndc;
988         size_t comp_len;
989
990         xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt));
991         if (xact == NULL) {
992                 if_printf(sc->hn_ifp, "no xact for RNDIS halt\n");
993                 return (ENXIO);
994         }
995         halt = vmbus_xact_req_data(xact);
996         halt->rm_type = REMOTE_NDIS_HALT_MSG;
997         halt->rm_len = sizeof(*halt);
998         halt->rm_rid = hn_rndis_rid(sc);
999
1000         /* No RNDIS completion; rely on NVS message send completion */
1001         hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact);
1002         hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len);
1003
1004         vmbus_xact_put(xact);
1005         if (bootverbose)
1006                 if_printf(sc->hn_ifp, "RNDIS halt done\n");
1007         return (0);
1008 }
1009
1010 /*
1011  * RNDIS filter on device add
1012  */
1013 int
1014 hv_rf_on_device_add(struct hn_softc *sc, void *additl_info,
1015     int *nchan0, int mtu)
1016 {
1017         int ret;
1018         netvsc_device_info *dev_info = (netvsc_device_info *)additl_info;
1019         device_t dev = sc->hn_dev;
1020         int nchan = *nchan0, rxr_cnt, nsubch;
1021
1022         ret = hn_nvs_attach(sc, mtu);
1023         if (ret != 0)
1024                 return (ret);
1025
1026         /*
1027          * Initialize the rndis device
1028          */
1029
1030         /* Send the rndis initialization message */
1031         ret = hv_rf_init_device(sc);
1032         if (ret != 0) {
1033                 /*
1034                  * TODO: If rndis init failed, we will need to shut down
1035                  * the channel
1036                  */
1037         }
1038
1039         /* Get the mac address */
1040         ret = hv_rf_query_device_mac(sc, dev_info->mac_addr);
1041         if (ret != 0) {
1042                 /* TODO: shut down rndis device and the channel */
1043         }
1044
1045         /* Configure NDIS offload settings */
1046         hn_rndis_conf_offload(sc);
1047
1048         hv_rf_query_device_link_status(sc, &dev_info->link_state);
1049
1050         if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30 || nchan == 1) {
1051                 /*
1052                  * Either RSS is not supported, or multiple RX/TX rings
1053                  * are not requested.
1054                  */
1055                 *nchan0 = 1;
1056                 return (0);
1057         }
1058
1059         /*
1060          * Get RSS capabilities, e.g. # of RX rings, and # of indirect
1061          * table entries.
1062          */
1063         ret = hn_rndis_get_rsscaps(sc, &rxr_cnt);
1064         if (ret) {
1065                 /* No RSS; this is benign. */
1066                 *nchan0 = 1;
1067                 return (0);
1068         }
1069         if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n",
1070             rxr_cnt, nchan);
1071
1072         if (nchan > rxr_cnt)
1073                 nchan = rxr_cnt;
1074         if (nchan == 1) {
1075                 device_printf(dev, "only 1 channel is supported, no vRSS\n");
1076                 *nchan0 = 1;
1077                 return (0);
1078         }
1079         
1080         /*
1081          * Allocate sub-channels from NVS.
1082          */
1083         nsubch = nchan - 1;
1084         ret = hn_nvs_alloc_subchans(sc, &nsubch);
1085         if (ret || nsubch == 0) {
1086                 /* Failed to allocate sub-channels. */
1087                 *nchan0 = 1;
1088                 return (0);
1089         }
1090         nchan = nsubch + 1;
1091
1092         /*
1093          * Configure RSS key and indirect table after all sub-channels
1094          * are allocated.
1095          */
1096         ret = hn_rndis_conf_rss(sc, nchan);
1097         if (ret != 0) {
1098                 /* Failed to configure RSS key or indirect table. */
1099                 nchan = 1;
1100         }
1101         *nchan0 = nchan;
1102         return (0);
1103 }
1104
1105 /*
1106  * RNDIS filter on device remove
1107  */
1108 int
1109 hv_rf_on_device_remove(struct hn_softc *sc)
1110 {
1111         int ret;
1112
1113         /* Halt and release the rndis device */
1114         ret = hv_rf_halt_device(sc);
1115
1116         /* Pass control to inner driver to remove the device */
1117         ret |= hv_nv_on_device_remove(sc);
1118
1119         return (ret);
1120 }
1121
1122 /*
1123  * RNDIS filter on open
1124  */
1125 int
1126 hv_rf_on_open(struct hn_softc *sc)
1127 {
1128         uint32_t filter;
1129
1130         /* XXX */
1131         if (hv_promisc_mode != 1) {
1132                 filter = NDIS_PACKET_TYPE_BROADCAST |
1133                     NDIS_PACKET_TYPE_ALL_MULTICAST |
1134                     NDIS_PACKET_TYPE_DIRECTED;
1135         } else {
1136                 filter = NDIS_PACKET_TYPE_PROMISCUOUS;
1137         }
1138         return (hn_rndis_set_rxfilter(sc, filter));
1139 }
1140
1141 /*
1142  * RNDIS filter on close
1143  */
1144 int 
1145 hv_rf_on_close(struct hn_softc *sc)
1146 {
1147
1148         return (hn_rndis_set_rxfilter(sc, 0));
1149 }
1150
1151 void
1152 hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
1153 {
1154
1155         hn_chan_rollup(rxr, txr);
1156 }