]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/hyperv/netvsc/hv_rndis_filter.c
hyperv/hn: Remove unnecessary NULL check.
[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 <sys/types.h>
42 #include <machine/atomic.h>
43 #include <sys/sema.h>
44 #include <vm/vm.h>
45 #include <vm/vm_param.h>
46 #include <vm/pmap.h>
47
48 #include <dev/hyperv/include/hyperv.h>
49 #include <dev/hyperv/include/vmbus_xact.h>
50 #include <dev/hyperv/netvsc/hv_net_vsc.h>
51 #include <dev/hyperv/netvsc/hv_rndis.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 rndis_msg *response);
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 /*
106  * Set the Per-Packet-Info with the specified type
107  */
108 void *
109 hv_set_rppi_data(rndis_msg *rndis_mesg, uint32_t rppi_size,
110         int pkt_type)
111 {
112         rndis_packet *rndis_pkt;
113         rndis_per_packet_info *rppi;
114
115         rndis_pkt = &rndis_mesg->msg.packet;
116         rndis_pkt->data_offset += rppi_size;
117
118         rppi = (rndis_per_packet_info *)((char *)rndis_pkt +
119             rndis_pkt->per_pkt_info_offset + rndis_pkt->per_pkt_info_length);
120
121         rppi->size = rppi_size;
122         rppi->type = pkt_type;
123         rppi->per_packet_info_offset = sizeof(rndis_per_packet_info);
124
125         rndis_pkt->per_pkt_info_length += rppi_size;
126
127         return (rppi);
128 }
129
130 /*
131  * Get the Per-Packet-Info with the specified type
132  * return NULL if not found.
133  */
134 void *
135 hv_get_ppi_data(rndis_packet *rpkt, uint32_t type)
136 {
137         rndis_per_packet_info *ppi;
138         int len;
139
140         if (rpkt->per_pkt_info_offset == 0)
141                 return (NULL);
142
143         ppi = (rndis_per_packet_info *)((unsigned long)rpkt +
144             rpkt->per_pkt_info_offset);
145         len = rpkt->per_pkt_info_length;
146
147         while (len > 0) {
148                 if (ppi->type == type)
149                         return (void *)((unsigned long)ppi +
150                             ppi->per_packet_info_offset);
151
152                 len -= ppi->size;
153                 ppi = (rndis_per_packet_info *)((unsigned long)ppi + ppi->size);
154         }
155
156         return (NULL);
157 }
158
159 /*
160  * RNDIS filter receive indicate status
161  */
162 static void 
163 hv_rf_receive_indicate_status(struct hn_softc *sc, const rndis_msg *response)
164 {
165         const rndis_indicate_status *indicate = &response->msg.indicate_status;
166                 
167         switch(indicate->status) {
168         case RNDIS_STATUS_MEDIA_CONNECT:
169                 netvsc_linkstatus_callback(sc, 1);
170                 break;
171         case RNDIS_STATUS_MEDIA_DISCONNECT:
172                 netvsc_linkstatus_callback(sc, 0);
173                 break;
174         default:
175                 /* TODO: */
176                 if_printf(sc->hn_ifp,
177                     "unknown status %d received\n", indicate->status);
178                 break;
179         }
180 }
181
182 static int
183 hv_rf_find_recvinfo(const rndis_packet *rpkt, struct hn_recvinfo *info)
184 {
185         const rndis_per_packet_info *ppi;
186         uint32_t mask, len;
187
188         info->vlan_info = NULL;
189         info->csum_info = NULL;
190         info->hash_info = NULL;
191         info->hash_value = NULL;
192
193         if (rpkt->per_pkt_info_offset == 0)
194                 return 0;
195
196         ppi = (const rndis_per_packet_info *)
197             ((const uint8_t *)rpkt + rpkt->per_pkt_info_offset);
198         len = rpkt->per_pkt_info_length;
199         mask = 0;
200
201         while (len != 0) {
202                 const void *ppi_dptr;
203                 uint32_t ppi_dlen;
204
205                 if (__predict_false(ppi->size < ppi->per_packet_info_offset))
206                         return EINVAL;
207                 ppi_dlen = ppi->size - ppi->per_packet_info_offset;
208                 ppi_dptr = (const uint8_t *)ppi + ppi->per_packet_info_offset;
209
210                 switch (ppi->type) {
211                 case ieee_8021q_info:
212                         if (__predict_false(ppi_dlen < sizeof(ndis_8021q_info)))
213                                 return EINVAL;
214                         info->vlan_info = ppi_dptr;
215                         mask |= HV_RF_RECVINFO_VLAN;
216                         break;
217
218                 case tcpip_chksum_info:
219                         if (__predict_false(ppi_dlen <
220                             sizeof(rndis_tcp_ip_csum_info)))
221                                 return EINVAL;
222                         info->csum_info = ppi_dptr;
223                         mask |= HV_RF_RECVINFO_CSUM;
224                         break;
225
226                 case nbl_hash_value:
227                         if (__predict_false(ppi_dlen <
228                             sizeof(struct rndis_hash_value)))
229                                 return EINVAL;
230                         info->hash_value = ppi_dptr;
231                         mask |= HV_RF_RECVINFO_HASHVAL;
232                         break;
233
234                 case nbl_hash_info:
235                         if (__predict_false(ppi_dlen <
236                             sizeof(struct rndis_hash_info)))
237                                 return EINVAL;
238                         info->hash_info = ppi_dptr;
239                         mask |= HV_RF_RECVINFO_HASHINF;
240                         break;
241
242                 default:
243                         goto skip;
244                 }
245
246                 if (mask == HV_RF_RECVINFO_ALL) {
247                         /* All found; done */
248                         break;
249                 }
250 skip:
251                 if (__predict_false(len < ppi->size))
252                         return EINVAL;
253                 len -= ppi->size;
254                 ppi = (const rndis_per_packet_info *)
255                     ((const uint8_t *)ppi + ppi->size);
256         }
257         return 0;
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 rndis_msg *message = data;
267         const rndis_packet *rndis_pkt;
268         uint32_t data_offset;
269         struct hn_recvinfo info;
270
271         rndis_pkt = &message->msg.packet;
272
273         /*
274          * Fixme:  Handle multiple rndis pkt msgs that may be enclosed in this
275          * netvsc packet (ie tot_data_buf_len != message_length)
276          */
277
278         /* Remove rndis header, then pass data packet up the stack */
279         data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
280
281         dlen -= data_offset;
282         if (dlen < rndis_pkt->data_length) {
283                 if_printf(rxr->hn_ifp,
284                     "total length %u is less than data length %u\n",
285                     dlen, rndis_pkt->data_length);
286                 return;
287         }
288
289         dlen = rndis_pkt->data_length;
290         data = (const uint8_t *)data + data_offset;
291
292         if (hv_rf_find_recvinfo(rndis_pkt, &info)) {
293                 if_printf(rxr->hn_ifp, "recvinfo parsing failed\n");
294                 return;
295         }
296         netvsc_recv(rxr, data, dlen, &info);
297 }
298
299 /*
300  * RNDIS filter on receive
301  */
302 int
303 hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
304     const void *data, int dlen)
305 {
306         const rndis_msg *rndis_hdr;
307         const struct rndis_comp_hdr *comp;
308
309         rndis_hdr = data;
310         switch (rndis_hdr->ndis_msg_type) {
311         /* data message */
312         case REMOTE_NDIS_PACKET_MSG:
313                 hv_rf_receive_data(rxr, data, dlen);
314                 break;
315
316         /* completion messages */
317         case REMOTE_NDIS_INITIALIZE_CMPLT:
318         case REMOTE_NDIS_QUERY_CMPLT:
319         case REMOTE_NDIS_SET_CMPLT:
320         case REMOTE_NDIS_KEEPALIVE_CMPLT:
321                 comp = data;
322                 KASSERT(comp->rm_rid > HN_RNDIS_RID_COMPAT_MAX,
323                     ("invalid rid 0x%08x\n", comp->rm_rid));
324                 vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen);
325                 break;
326
327         /* notification message */
328         case REMOTE_NDIS_INDICATE_STATUS_MSG:
329                 hv_rf_receive_indicate_status(sc, rndis_hdr);
330                 break;
331
332         case REMOTE_NDIS_RESET_CMPLT:
333                 /*
334                  * Reset completed, no rid.
335                  *
336                  * NOTE:
337                  * RESET is not issued by hn(4), so this message should
338                  * _not_ be observed.
339                  */
340                 if_printf(sc->hn_ifp, "RESET CMPLT received\n");
341                 break;
342
343         default:
344                 if_printf(sc->hn_ifp, "unknown RNDIS message 0x%x\n",
345                         rndis_hdr->ndis_msg_type);
346                 break;
347         }
348         return (0);
349 }
350
351 /*
352  * RNDIS filter query device MAC address
353  */
354 static int
355 hv_rf_query_device_mac(struct hn_softc *sc, uint8_t *eaddr)
356 {
357         size_t eaddr_len;
358         int error;
359
360         eaddr_len = ETHER_ADDR_LEN;
361         error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0,
362             eaddr, &eaddr_len);
363         if (error)
364                 return (error);
365         if (eaddr_len != ETHER_ADDR_LEN) {
366                 if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len);
367                 return (EINVAL);
368         }
369         return (0);
370 }
371
372 /*
373  * RNDIS filter query device link status
374  */
375 static int
376 hv_rf_query_device_link_status(struct hn_softc *sc, uint32_t *link_status)
377 {
378         size_t size;
379         int error;
380
381         size = sizeof(*link_status);
382         error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0,
383             link_status, &size);
384         if (error)
385                 return (error);
386         if (size != sizeof(uint32_t)) {
387                 if_printf(sc->hn_ifp, "invalid link status len %zu\n", size);
388                 return (EINVAL);
389         }
390         return (0);
391 }
392
393 static uint8_t netvsc_hash_key[NDIS_HASH_KEYSIZE_TOEPLITZ] = {
394         0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
395         0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
396         0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
397         0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
398         0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
399 };
400
401 static const void *
402 hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen,
403     struct hn_send_ctx *sndc, size_t *comp_len)
404 {
405         struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT];
406         int gpa_cnt, error;
407         bus_addr_t paddr;
408
409         KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0,
410             ("invalid request length %zu", reqlen));
411
412         /*
413          * Setup the SG list.
414          */
415         paddr = vmbus_xact_req_paddr(xact);
416         KASSERT((paddr & PAGE_MASK) == 0,
417             ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr));
418         for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) {
419                 int len = PAGE_SIZE;
420
421                 if (reqlen == 0)
422                         break;
423                 if (reqlen < len)
424                         len = reqlen;
425
426                 gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt;
427                 gpa[gpa_cnt].gpa_len = len;
428                 gpa[gpa_cnt].gpa_ofs = 0;
429
430                 reqlen -= len;
431         }
432         KASSERT(reqlen == 0, ("still have %zu request data left", reqlen));
433
434         /*
435          * Send this RNDIS control message and wait for its completion
436          * message.
437          */
438         vmbus_xact_activate(xact);
439         error = hv_nv_on_send(sc->hn_prichan, HN_NVS_RNDIS_MTYPE_CTRL, sndc,
440             gpa, gpa_cnt);
441         if (error) {
442                 vmbus_xact_deactivate(xact);
443                 if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error);
444                 return (NULL);
445         }
446         return (vmbus_xact_wait(xact, comp_len));
447 }
448
449 static const void *
450 hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid,
451     size_t reqlen, size_t *comp_len0, uint32_t comp_type)
452 {
453         const struct rndis_comp_hdr *comp;
454         size_t comp_len, min_complen = *comp_len0;
455
456         KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid));
457         KASSERT(min_complen >= sizeof(*comp),
458             ("invalid minimum complete len %zu", min_complen));
459
460         /*
461          * Execute the xact setup by the caller.
462          */
463         comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_send_ctx_none,
464             &comp_len);
465         if (comp == NULL)
466                 return (NULL);
467
468         /*
469          * Check this RNDIS complete message.
470          */
471         if (comp_len < min_complen) {
472                 if (comp_len >= sizeof(*comp)) {
473                         /* rm_status field is valid */
474                         if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, "
475                             "status 0x%08x\n", comp_len, comp->rm_status);
476                 } else {
477                         if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n",
478                             comp_len);
479                 }
480                 return (NULL);
481         }
482         if (comp->rm_len < min_complen) {
483                 if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n",
484                     comp->rm_len);
485                 return (NULL);
486         }
487         if (comp->rm_type != comp_type) {
488                 if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, "
489                     "expect 0x%08x\n", comp->rm_type, comp_type);
490                 return (NULL);
491         }
492         if (comp->rm_rid != rid) {
493                 if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, "
494                     "expect %u\n", comp->rm_rid, rid);
495                 return (NULL);
496         }
497         /* All pass! */
498         *comp_len0 = comp_len;
499         return (comp);
500 }
501
502 static int
503 hn_rndis_query(struct hn_softc *sc, uint32_t oid,
504     const void *idata, size_t idlen, void *odata, size_t *odlen0)
505 {
506         struct rndis_query_req *req;
507         const struct rndis_query_comp *comp;
508         struct vmbus_xact *xact;
509         size_t reqlen, odlen = *odlen0, comp_len;
510         int error, ofs;
511         uint32_t rid;
512
513         reqlen = sizeof(*req) + idlen;
514         xact = vmbus_xact_get(sc->hn_xact, reqlen);
515         if (xact == NULL) {
516                 if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid);
517                 return (ENXIO);
518         }
519         rid = hn_rndis_rid(sc);
520         req = vmbus_xact_req_data(xact);
521         req->rm_type = REMOTE_NDIS_QUERY_MSG;
522         req->rm_len = reqlen;
523         req->rm_rid = rid;
524         req->rm_oid = oid;
525         /*
526          * XXX
527          * This is _not_ RNDIS Spec conforming:
528          * "This MUST be set to 0 when there is no input data
529          *  associated with the OID."
530          *
531          * If this field was set to 0 according to the RNDIS Spec,
532          * Hyper-V would set non-SUCCESS status in the query
533          * completion.
534          */
535         req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET;
536
537         if (idlen > 0) {
538                 req->rm_infobuflen = idlen;
539                 /* Input data immediately follows RNDIS query. */
540                 memcpy(req + 1, idata, idlen);
541         }
542
543         comp_len = sizeof(*comp) + odlen;
544         comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
545             REMOTE_NDIS_QUERY_CMPLT);
546         if (comp == NULL) {
547                 if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid);
548                 error = EIO;
549                 goto done;
550         }
551
552         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
553                 if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: "
554                     "status 0x%08x\n", oid, comp->rm_status);
555                 error = EIO;
556                 goto done;
557         }
558         if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) {
559                 /* No output data! */
560                 if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid);
561                 *odlen0 = 0;
562                 error = 0;
563                 goto done;
564         }
565
566         /*
567          * Check output data length and offset.
568          */
569         /* ofs is the offset from the beginning of comp. */
570         ofs = RNDIS_QUERY_COMP_INFOBUFABS(comp->rm_infobufoffset);
571         if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
572                 if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
573                     "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);
574                 error = EINVAL;
575                 goto done;
576         }
577
578         /*
579          * Save output data.
580          */
581         if (comp->rm_infobuflen < odlen)
582                 odlen = comp->rm_infobuflen;
583         memcpy(odata, ((const uint8_t *)comp) + ofs, odlen);
584         *odlen0 = odlen;
585
586         error = 0;
587 done:
588         vmbus_xact_put(xact);
589         return (error);
590 }
591
592 static int
593 hn_rndis_get_rsscaps(struct hn_softc *sc, int *rxr_cnt)
594 {
595         struct ndis_rss_caps in, caps;
596         size_t caps_len;
597         int error;
598
599         /*
600          * Only NDIS 6.30+ is supported.
601          */
602         KASSERT(sc->hn_ndis_ver >= NDIS_VERSION_6_30,
603             ("NDIS 6.30+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
604         *rxr_cnt = 0;
605
606         memset(&in, 0, sizeof(in));
607         in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS;
608         in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
609         in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
610
611         caps_len = NDIS_RSS_CAPS_SIZE;
612         error = hn_rndis_query(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
613             &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len);
614         if (error)
615                 return (error);
616         if (caps_len < NDIS_RSS_CAPS_SIZE_6_0) {
617                 if_printf(sc->hn_ifp, "invalid NDIS RSS caps len %zu",
618                     caps_len);
619                 return (EINVAL);
620         }
621
622         if (caps.ndis_nrxr == 0) {
623                 if_printf(sc->hn_ifp, "0 RX rings!?\n");
624                 return (EINVAL);
625         }
626         *rxr_cnt = caps.ndis_nrxr;
627
628         if (caps_len == NDIS_RSS_CAPS_SIZE) {
629                 if (bootverbose) {
630                         if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
631                             caps.ndis_nind);
632                 }
633         }
634         return (0);
635 }
636
637 static int
638 hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen)
639 {
640         struct rndis_set_req *req;
641         const struct rndis_set_comp *comp;
642         struct vmbus_xact *xact;
643         size_t reqlen, comp_len;
644         uint32_t rid;
645         int error;
646
647         KASSERT(dlen > 0, ("invalid dlen %zu", dlen));
648
649         reqlen = sizeof(*req) + dlen;
650         xact = vmbus_xact_get(sc->hn_xact, reqlen);
651         if (xact == NULL) {
652                 if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid);
653                 return (ENXIO);
654         }
655         rid = hn_rndis_rid(sc);
656         req = vmbus_xact_req_data(xact);
657         req->rm_type = REMOTE_NDIS_SET_MSG;
658         req->rm_len = reqlen;
659         req->rm_rid = rid;
660         req->rm_oid = oid;
661         req->rm_infobuflen = dlen;
662         req->rm_infobufoffset = RNDIS_SET_REQ_INFOBUFOFFSET;
663         /* Data immediately follows RNDIS set. */
664         memcpy(req + 1, data, dlen);
665
666         comp_len = sizeof(*comp);
667         comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
668             REMOTE_NDIS_SET_CMPLT);
669         if (comp == NULL) {
670                 if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid);
671                 error = EIO;
672                 goto done;
673         }
674
675         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
676                 if_printf(sc->hn_ifp, "RNDIS set 0x%08x failed: "
677                     "status 0x%08x\n", oid, comp->rm_status);
678                 error = EIO;
679                 goto done;
680         }
681         error = 0;
682 done:
683         vmbus_xact_put(xact);
684         return (error);
685 }
686
687 static int
688 hn_rndis_conf_offload(struct hn_softc *sc)
689 {
690         struct ndis_offload_params params;
691         size_t paramsz;
692         int error;
693
694         /* NOTE: 0 means "no change" */
695         memset(&params, 0, sizeof(params));
696
697         params.ndis_hdr.ndis_type = NDIS_OBJTYPE_DEFAULT;
698         if (sc->hn_ndis_ver < NDIS_VERSION_6_30) {
699                 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_2;
700                 paramsz = NDIS_OFFLOAD_PARAMS_SIZE_6_1;
701         } else {
702                 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3;
703                 paramsz = NDIS_OFFLOAD_PARAMS_SIZE;
704         }
705         params.ndis_hdr.ndis_size = paramsz;
706
707         params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX;
708         params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX;
709         params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX;
710         if (sc->hn_ndis_ver >= NDIS_VERSION_6_30) {
711                 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX;
712                 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX;
713         }
714         params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON;
715         /* XXX ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON */
716
717         error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, &params, paramsz);
718         if (error) {
719                 if_printf(sc->hn_ifp, "offload config failed: %d\n", error);
720         } else {
721                 if (bootverbose)
722                         if_printf(sc->hn_ifp, "offload config done\n");
723         }
724         return (error);
725 }
726
727 static int
728 hn_rndis_conf_rss(struct hn_softc *sc, int nchan)
729 {
730         struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
731         struct ndis_rss_params *prm = &rss->rss_params;
732         int i, error;
733
734         /*
735          * Only NDIS 6.30+ is supported.
736          */
737         KASSERT(sc->hn_ndis_ver >= NDIS_VERSION_6_30,
738             ("NDIS 6.30+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
739
740         memset(rss, 0, sizeof(*rss));
741         prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS;
742         prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2;
743         prm->ndis_hdr.ndis_size = sizeof(*rss);
744         prm->ndis_hash = NDIS_HASH_FUNCTION_TOEPLITZ |
745             NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4 |
746             NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6;
747         /* TODO: Take ndis_rss_caps.ndis_nind into account */
748         prm->ndis_indsize = sizeof(rss->rss_ind);
749         prm->ndis_indoffset =
750             __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]);
751         prm->ndis_keysize = sizeof(rss->rss_key);
752         prm->ndis_keyoffset =
753             __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]);
754
755         /* Setup RSS key */
756         memcpy(rss->rss_key, netvsc_hash_key, sizeof(rss->rss_key));
757
758         /* Setup RSS indirect table */
759         /* TODO: Take ndis_rss_caps.ndis_nind into account */
760         for (i = 0; i < NDIS_HASH_INDCNT; ++i)
761                 rss->rss_ind[i] = i % nchan;
762
763         error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS,
764             rss, sizeof(*rss));
765         if (error) {
766                 if_printf(sc->hn_ifp, "RSS config failed: %d\n", error);
767         } else {
768                 if (bootverbose)
769                         if_printf(sc->hn_ifp, "RSS config done\n");
770         }
771         return (error);
772 }
773
774 static int
775 hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter)
776 {
777         int error;
778
779         error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER,
780             &filter, sizeof(filter));
781         if (error) {
782                 if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n",
783                     filter, error);
784         } else {
785                 if (bootverbose) {
786                         if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n",
787                             filter);
788                 }
789         }
790         return (error);
791 }
792
793 /*
794  * RNDIS filter init device
795  */
796 static int
797 hv_rf_init_device(struct hn_softc *sc)
798 {
799         struct rndis_init_req *req;
800         const struct rndis_init_comp *comp;
801         struct vmbus_xact *xact;
802         size_t comp_len;
803         uint32_t rid;
804         int error;
805
806         xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
807         if (xact == NULL) {
808                 if_printf(sc->hn_ifp, "no xact for RNDIS init\n");
809                 return (ENXIO);
810         }
811         rid = hn_rndis_rid(sc);
812         req = vmbus_xact_req_data(xact);
813         req->rm_type = REMOTE_NDIS_INITIALIZE_MSG;
814         req->rm_len = sizeof(*req);
815         req->rm_rid = rid;
816         req->rm_ver_major = RNDIS_VERSION_MAJOR;
817         req->rm_ver_minor = RNDIS_VERSION_MINOR;
818         req->rm_max_xfersz = HN_RNDIS_XFER_SIZE;
819
820         comp_len = RNDIS_INIT_COMP_SIZE_MIN;
821         comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len,
822             REMOTE_NDIS_INITIALIZE_CMPLT);
823         if (comp == NULL) {
824                 if_printf(sc->hn_ifp, "exec RNDIS init failed\n");
825                 error = EIO;
826                 goto done;
827         }
828
829         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
830                 if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n",
831                     comp->rm_status);
832                 error = EIO;
833                 goto done;
834         }
835         if (bootverbose) {
836                 if_printf(sc->hn_ifp, "RNDIS ver %u.%u, pktsz %u, pktcnt %u, "
837                     "align %u\n", comp->rm_ver_major, comp->rm_ver_minor,
838                     comp->rm_pktmaxsz, comp->rm_pktmaxcnt,
839                     1U << comp->rm_align);
840         }
841         error = 0;
842 done:
843         vmbus_xact_put(xact);
844         return (error);
845 }
846
847 /*
848  * RNDIS filter halt device
849  */
850 static int
851 hv_rf_halt_device(struct hn_softc *sc)
852 {
853         struct vmbus_xact *xact;
854         struct rndis_halt_req *halt;
855         struct hn_send_ctx sndc;
856         size_t comp_len;
857
858         xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt));
859         if (xact == NULL) {
860                 if_printf(sc->hn_ifp, "no xact for RNDIS halt\n");
861                 return (ENXIO);
862         }
863         halt = vmbus_xact_req_data(xact);
864         halt->rm_type = REMOTE_NDIS_HALT_MSG;
865         halt->rm_len = sizeof(*halt);
866         halt->rm_rid = hn_rndis_rid(sc);
867
868         /* No RNDIS completion; rely on NVS message send completion */
869         hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact);
870         hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len);
871
872         vmbus_xact_put(xact);
873         if (bootverbose)
874                 if_printf(sc->hn_ifp, "RNDIS halt done\n");
875         return (0);
876 }
877
878 /*
879  * RNDIS filter on device add
880  */
881 int
882 hv_rf_on_device_add(struct hn_softc *sc, void *additl_info,
883     int *nchan0, struct hn_rx_ring *rxr)
884 {
885         int ret;
886         netvsc_device_info *dev_info = (netvsc_device_info *)additl_info;
887         device_t dev = sc->hn_dev;
888         struct hn_nvs_subch_req *req;
889         const struct hn_nvs_subch_resp *resp;
890         size_t resp_len;
891         struct vmbus_xact *xact = NULL;
892         uint32_t status, nsubch;
893         int nchan = *nchan0;
894         int rxr_cnt;
895
896         /*
897          * Let the inner driver handle this first to create the netvsc channel
898          * NOTE! Once the channel is created, we may get a receive callback 
899          * (hv_rf_on_receive()) before this call is completed.
900          * Note:  Earlier code used a function pointer here.
901          */
902         ret = hv_nv_on_device_add(sc, rxr);
903         if (ret != 0)
904                 return (ret);
905
906         /*
907          * Initialize the rndis device
908          */
909
910         /* Send the rndis initialization message */
911         ret = hv_rf_init_device(sc);
912         if (ret != 0) {
913                 /*
914                  * TODO: If rndis init failed, we will need to shut down
915                  * the channel
916                  */
917         }
918
919         /* Get the mac address */
920         ret = hv_rf_query_device_mac(sc, dev_info->mac_addr);
921         if (ret != 0) {
922                 /* TODO: shut down rndis device and the channel */
923         }
924
925         /* Configure NDIS offload settings */
926         hn_rndis_conf_offload(sc);
927
928         hv_rf_query_device_link_status(sc, &dev_info->link_state);
929
930         if (sc->hn_ndis_ver < NDIS_VERSION_6_30 || nchan == 1) {
931                 /*
932                  * Either RSS is not supported, or multiple RX/TX rings
933                  * are not requested.
934                  */
935                 *nchan0 = 1;
936                 return (0);
937         }
938
939         /*
940          * Get RSS capabilities, e.g. # of RX rings, and # of indirect
941          * table entries.
942          */
943         ret = hn_rndis_get_rsscaps(sc, &rxr_cnt);
944         if (ret) {
945                 /* No RSS; this is benign. */
946                 *nchan0 = 1;
947                 return (0);
948         }
949         if (nchan > rxr_cnt)
950                 nchan = rxr_cnt;
951         if_printf(sc->hn_ifp, "RX rings offered %u, requested %d\n",
952             rxr_cnt, nchan);
953
954         if (nchan == 1) {
955                 device_printf(dev, "only 1 channel is supported, no vRSS\n");
956                 goto out;
957         }
958         
959         /*
960          * Ask NVS to allocate sub-channels.
961          */
962         xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
963         if (xact == NULL) {
964                 if_printf(sc->hn_ifp, "no xact for nvs subch req\n");
965                 ret = ENXIO;
966                 goto out;
967         }
968         req = vmbus_xact_req_data(xact);
969         req->nvs_type = HN_NVS_TYPE_SUBCH_REQ;
970         req->nvs_op = HN_NVS_SUBCH_OP_ALLOC;
971         req->nvs_nsubch = nchan - 1;
972
973         resp = hn_nvs_xact_execute(sc, xact, req, sizeof(*req), &resp_len);
974         if (resp == NULL) {
975                 if_printf(sc->hn_ifp, "exec subch failed\n");
976                 ret = EIO;
977                 goto out;
978         }
979         if (resp_len < sizeof(*resp)) {
980                 if_printf(sc->hn_ifp, "invalid subch resp length %zu\n",
981                     resp_len);
982                 ret = EINVAL;
983                 goto out;
984         }
985         if (resp->nvs_type != HN_NVS_TYPE_SUBCH_RESP) {
986                 if_printf(sc->hn_ifp, "not subch resp, type %u\n",
987                     resp->nvs_type);
988                 ret = EINVAL;
989                 goto out;
990         }
991
992         status = resp->nvs_status;
993         nsubch = resp->nvs_nsubch;
994         vmbus_xact_put(xact);
995         xact = NULL;
996
997         if (status != HN_NVS_STATUS_OK) {
998                 if_printf(sc->hn_ifp, "subch req failed: %x\n", status);
999                 ret = EIO;
1000                 goto out;
1001         }
1002         if (nsubch > nchan - 1) {
1003                 if_printf(sc->hn_ifp, "%u subchans are allocated, requested %u\n",
1004                     nsubch, nchan - 1);
1005                 nsubch = nchan - 1;
1006         }
1007         nchan = nsubch + 1;
1008
1009         ret = hn_rndis_conf_rss(sc, nchan);
1010         if (ret != 0)
1011                 *nchan0 = 1;
1012         else
1013                 *nchan0 = nchan;
1014 out:
1015         if (xact != NULL)
1016                 vmbus_xact_put(xact);
1017         return (ret);
1018 }
1019
1020 /*
1021  * RNDIS filter on device remove
1022  */
1023 int
1024 hv_rf_on_device_remove(struct hn_softc *sc)
1025 {
1026         int ret;
1027
1028         /* Halt and release the rndis device */
1029         ret = hv_rf_halt_device(sc);
1030
1031         /* Pass control to inner driver to remove the device */
1032         ret |= hv_nv_on_device_remove(sc);
1033
1034         return (ret);
1035 }
1036
1037 /*
1038  * RNDIS filter on open
1039  */
1040 int
1041 hv_rf_on_open(struct hn_softc *sc)
1042 {
1043         uint32_t filter;
1044
1045         /* XXX */
1046         if (hv_promisc_mode != 1) {
1047                 filter = NDIS_PACKET_TYPE_BROADCAST |
1048                     NDIS_PACKET_TYPE_ALL_MULTICAST |
1049                     NDIS_PACKET_TYPE_DIRECTED;
1050         } else {
1051                 filter = NDIS_PACKET_TYPE_PROMISCUOUS;
1052         }
1053         return (hn_rndis_set_rxfilter(sc, filter));
1054 }
1055
1056 /*
1057  * RNDIS filter on close
1058  */
1059 int 
1060 hv_rf_on_close(struct hn_softc *sc)
1061 {
1062
1063         return (hn_rndis_set_rxfilter(sc, 0));
1064 }
1065
1066 void
1067 hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
1068 {
1069
1070         netvsc_channel_rollup(rxr, txr);
1071 }