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