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