]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/netvsc/hn_rndis.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / sys / dev / hyperv / netvsc / hn_rndis.c
1 /*-
2  * Copyright (c) 2009-2012,2016-2017 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 "opt_inet6.h"
33 #include "opt_inet.h"
34
35 #include <sys/param.h>
36 #include <sys/socket.h>
37 #include <sys/systm.h>
38 #include <sys/taskqueue.h>
39
40 #include <machine/atomic.h>
41
42 #include <net/ethernet.h>
43 #include <net/if.h>
44 #include <net/if_arp.h>
45 #include <net/if_var.h>
46 #include <net/if_media.h>
47 #include <net/rndis.h>
48
49 #include <netinet/in.h>
50 #include <netinet/ip.h>
51 #include <netinet/tcp_lro.h>
52
53 #include <dev/hyperv/include/hyperv.h>
54 #include <dev/hyperv/include/hyperv_busdma.h>
55 #include <dev/hyperv/include/vmbus.h>
56 #include <dev/hyperv/include/vmbus_xact.h>
57
58 #include <dev/hyperv/netvsc/ndis.h>
59 #include <dev/hyperv/netvsc/if_hnreg.h>
60 #include <dev/hyperv/netvsc/if_hnvar.h>
61 #include <dev/hyperv/netvsc/hn_nvs.h>
62 #include <dev/hyperv/netvsc/hn_rndis.h>
63
64 #define HN_RNDIS_RID_COMPAT_MASK        0xffff
65 #define HN_RNDIS_RID_COMPAT_MAX         HN_RNDIS_RID_COMPAT_MASK
66
67 #define HN_RNDIS_XFER_SIZE              2048
68
69 #define HN_NDIS_TXCSUM_CAP_IP4          \
70         (NDIS_TXCSUM_CAP_IP4 | NDIS_TXCSUM_CAP_IP4OPT)
71 #define HN_NDIS_TXCSUM_CAP_TCP4         \
72         (NDIS_TXCSUM_CAP_TCP4 | NDIS_TXCSUM_CAP_TCP4OPT)
73 #define HN_NDIS_TXCSUM_CAP_TCP6         \
74         (NDIS_TXCSUM_CAP_TCP6 | NDIS_TXCSUM_CAP_TCP6OPT | \
75          NDIS_TXCSUM_CAP_IP6EXT)
76 #define HN_NDIS_TXCSUM_CAP_UDP6         \
77         (NDIS_TXCSUM_CAP_UDP6 | NDIS_TXCSUM_CAP_IP6EXT)
78 #define HN_NDIS_LSOV2_CAP_IP6           \
79         (NDIS_LSOV2_CAP_IP6EXT | NDIS_LSOV2_CAP_TCP6OPT)
80
81 static const void       *hn_rndis_xact_exec1(struct hn_softc *,
82                             struct vmbus_xact *, size_t,
83                             struct hn_nvs_sendctx *, size_t *);
84 static const void       *hn_rndis_xact_execute(struct hn_softc *,
85                             struct vmbus_xact *, uint32_t, size_t, size_t *,
86                             uint32_t);
87 static int              hn_rndis_query(struct hn_softc *, uint32_t,
88                             const void *, size_t, void *, size_t *);
89 static int              hn_rndis_query2(struct hn_softc *, uint32_t,
90                             const void *, size_t, void *, size_t *, size_t);
91 static int              hn_rndis_set(struct hn_softc *, uint32_t,
92                             const void *, size_t);
93 static int              hn_rndis_init(struct hn_softc *);
94 static int              hn_rndis_halt(struct hn_softc *);
95 static int              hn_rndis_conf_offload(struct hn_softc *, int);
96 static int              hn_rndis_query_hwcaps(struct hn_softc *,
97                             struct ndis_offload *);
98
99 static __inline uint32_t
100 hn_rndis_rid(struct hn_softc *sc)
101 {
102         uint32_t rid;
103
104 again:
105         rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1);
106         if (rid == 0)
107                 goto again;
108
109         /* Use upper 16 bits for non-compat RNDIS messages. */
110         return ((rid & 0xffff) << 16);
111 }
112
113 void
114 hn_rndis_rx_ctrl(struct hn_softc *sc, const void *data, int dlen)
115 {
116         const struct rndis_comp_hdr *comp;
117         const struct rndis_msghdr *hdr;
118
119         KASSERT(dlen >= sizeof(*hdr), ("invalid RNDIS msg\n"));
120         hdr = data;
121
122         switch (hdr->rm_type) {
123         case REMOTE_NDIS_INITIALIZE_CMPLT:
124         case REMOTE_NDIS_QUERY_CMPLT:
125         case REMOTE_NDIS_SET_CMPLT:
126         case REMOTE_NDIS_KEEPALIVE_CMPLT:       /* unused */
127                 if (dlen < sizeof(*comp)) {
128                         if_printf(sc->hn_ifp, "invalid RNDIS cmplt\n");
129                         return;
130                 }
131                 comp = data;
132
133                 KASSERT(comp->rm_rid > HN_RNDIS_RID_COMPAT_MAX,
134                     ("invalid RNDIS rid 0x%08x\n", comp->rm_rid));
135                 vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen);
136                 break;
137
138         case REMOTE_NDIS_RESET_CMPLT:
139                 /*
140                  * Reset completed, no rid.
141                  *
142                  * NOTE:
143                  * RESET is not issued by hn(4), so this message should
144                  * _not_ be observed.
145                  */
146                 if_printf(sc->hn_ifp, "RESET cmplt received\n");
147                 break;
148
149         default:
150                 if_printf(sc->hn_ifp, "unknown RNDIS msg 0x%x\n",
151                     hdr->rm_type);
152                 break;
153         }
154 }
155
156 int
157 hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr)
158 {
159         size_t eaddr_len;
160         int error;
161
162         eaddr_len = ETHER_ADDR_LEN;
163         error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0,
164             eaddr, &eaddr_len);
165         if (error)
166                 return (error);
167         if (eaddr_len != ETHER_ADDR_LEN) {
168                 if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len);
169                 return (EINVAL);
170         }
171         return (0);
172 }
173
174 int
175 hn_rndis_get_linkstatus(struct hn_softc *sc, uint32_t *link_status)
176 {
177         size_t size;
178         int error;
179
180         size = sizeof(*link_status);
181         error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0,
182             link_status, &size);
183         if (error)
184                 return (error);
185         if (size != sizeof(uint32_t)) {
186                 if_printf(sc->hn_ifp, "invalid link status len %zu\n", size);
187                 return (EINVAL);
188         }
189         return (0);
190 }
191
192 int
193 hn_rndis_get_mtu(struct hn_softc *sc, uint32_t *mtu)
194 {
195         size_t size;
196         int error;
197
198         size = sizeof(*mtu);
199         error = hn_rndis_query(sc, OID_GEN_MAXIMUM_FRAME_SIZE, NULL, 0,
200             mtu, &size);
201         if (error)
202                 return (error);
203         if (size != sizeof(uint32_t)) {
204                 if_printf(sc->hn_ifp, "invalid mtu len %zu\n", size);
205                 return (EINVAL);
206         }
207         return (0);
208 }
209
210 static const void *
211 hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen,
212     struct hn_nvs_sendctx *sndc, size_t *comp_len)
213 {
214         struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT];
215         int gpa_cnt, error;
216         bus_addr_t paddr;
217
218         KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0,
219             ("invalid request length %zu", reqlen));
220
221         /*
222          * Setup the SG list.
223          */
224         paddr = vmbus_xact_req_paddr(xact);
225         KASSERT((paddr & PAGE_MASK) == 0,
226             ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr));
227         for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) {
228                 int len = PAGE_SIZE;
229
230                 if (reqlen == 0)
231                         break;
232                 if (reqlen < len)
233                         len = reqlen;
234
235                 gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt;
236                 gpa[gpa_cnt].gpa_len = len;
237                 gpa[gpa_cnt].gpa_ofs = 0;
238
239                 reqlen -= len;
240         }
241         KASSERT(reqlen == 0, ("still have %zu request data left", reqlen));
242
243         /*
244          * Send this RNDIS control message and wait for its completion
245          * message.
246          */
247         vmbus_xact_activate(xact);
248         error = hn_nvs_send_rndis_ctrl(sc->hn_prichan, sndc, gpa, gpa_cnt);
249         if (error) {
250                 vmbus_xact_deactivate(xact);
251                 if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error);
252                 return (NULL);
253         }
254         return (vmbus_chan_xact_wait(sc->hn_prichan, xact, comp_len,
255             HN_CAN_SLEEP(sc)));
256 }
257
258 static const void *
259 hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid,
260     size_t reqlen, size_t *comp_len0, uint32_t comp_type)
261 {
262         const struct rndis_comp_hdr *comp;
263         size_t comp_len, min_complen = *comp_len0;
264
265         KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid));
266         KASSERT(min_complen >= sizeof(*comp),
267             ("invalid minimum complete len %zu", min_complen));
268
269         /*
270          * Execute the xact setup by the caller.
271          */
272         comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_nvs_sendctx_none,
273             &comp_len);
274         if (comp == NULL)
275                 return (NULL);
276
277         /*
278          * Check this RNDIS complete message.
279          */
280         if (comp_len < min_complen) {
281                 if (comp_len >= sizeof(*comp)) {
282                         /* rm_status field is valid */
283                         if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, "
284                             "status 0x%08x\n", comp_len, comp->rm_status);
285                 } else {
286                         if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n",
287                             comp_len);
288                 }
289                 return (NULL);
290         }
291         if (comp->rm_len < min_complen) {
292                 if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n",
293                     comp->rm_len);
294                 return (NULL);
295         }
296         if (comp->rm_type != comp_type) {
297                 if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, "
298                     "expect 0x%08x\n", comp->rm_type, comp_type);
299                 return (NULL);
300         }
301         if (comp->rm_rid != rid) {
302                 if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, "
303                     "expect %u\n", comp->rm_rid, rid);
304                 return (NULL);
305         }
306         /* All pass! */
307         *comp_len0 = comp_len;
308         return (comp);
309 }
310
311 static int
312 hn_rndis_query(struct hn_softc *sc, uint32_t oid,
313     const void *idata, size_t idlen, void *odata, size_t *odlen0)
314 {
315
316         return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0));
317 }
318
319 static int
320 hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
321     const void *idata, size_t idlen, void *odata, size_t *odlen0,
322     size_t min_odlen)
323 {
324         struct rndis_query_req *req;
325         const struct rndis_query_comp *comp;
326         struct vmbus_xact *xact;
327         size_t reqlen, odlen = *odlen0, comp_len;
328         int error, ofs;
329         uint32_t rid;
330
331         reqlen = sizeof(*req) + idlen;
332         xact = vmbus_xact_get(sc->hn_xact, reqlen);
333         if (xact == NULL) {
334                 if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid);
335                 return (ENXIO);
336         }
337         rid = hn_rndis_rid(sc);
338         req = vmbus_xact_req_data(xact);
339         req->rm_type = REMOTE_NDIS_QUERY_MSG;
340         req->rm_len = reqlen;
341         req->rm_rid = rid;
342         req->rm_oid = oid;
343         /*
344          * XXX
345          * This is _not_ RNDIS Spec conforming:
346          * "This MUST be set to 0 when there is no input data
347          *  associated with the OID."
348          *
349          * If this field was set to 0 according to the RNDIS Spec,
350          * Hyper-V would set non-SUCCESS status in the query
351          * completion.
352          */
353         req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET;
354
355         if (idlen > 0) {
356                 req->rm_infobuflen = idlen;
357                 /* Input data immediately follows RNDIS query. */
358                 memcpy(req + 1, idata, idlen);
359         }
360
361         comp_len = sizeof(*comp) + min_odlen;
362         comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
363             REMOTE_NDIS_QUERY_CMPLT);
364         if (comp == NULL) {
365                 if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid);
366                 error = EIO;
367                 goto done;
368         }
369
370         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
371                 if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: "
372                     "status 0x%08x\n", oid, comp->rm_status);
373                 error = EIO;
374                 goto done;
375         }
376         if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) {
377                 /* No output data! */
378                 if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid);
379                 *odlen0 = 0;
380                 error = 0;
381                 goto done;
382         }
383
384         /*
385          * Check output data length and offset.
386          */
387         /* ofs is the offset from the beginning of comp. */
388         ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset);
389         if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
390                 if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
391                     "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);
392                 error = EINVAL;
393                 goto done;
394         }
395
396         /*
397          * Save output data.
398          */
399         if (comp->rm_infobuflen < odlen)
400                 odlen = comp->rm_infobuflen;
401         memcpy(odata, ((const uint8_t *)comp) + ofs, odlen);
402         *odlen0 = odlen;
403
404         error = 0;
405 done:
406         vmbus_xact_put(xact);
407         return (error);
408 }
409
410 int
411 hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0)
412 {
413         struct ndis_rss_caps in, caps;
414         size_t caps_len;
415         int error, indsz, rxr_cnt, hash_fnidx;
416         uint32_t hash_func = 0, hash_types = 0;
417
418         *rxr_cnt0 = 0;
419
420         if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20)
421                 return (EOPNOTSUPP);
422
423         memset(&in, 0, sizeof(in));
424         in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS;
425         in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
426         in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
427
428         caps_len = NDIS_RSS_CAPS_SIZE;
429         error = hn_rndis_query2(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
430             &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len, NDIS_RSS_CAPS_SIZE_6_0);
431         if (error)
432                 return (error);
433
434         /*
435          * Preliminary verification.
436          */
437         if (caps.ndis_hdr.ndis_type != NDIS_OBJTYPE_RSS_CAPS) {
438                 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
439                     caps.ndis_hdr.ndis_type);
440                 return (EINVAL);
441         }
442         if (caps.ndis_hdr.ndis_rev < NDIS_RSS_CAPS_REV_1) {
443                 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
444                     caps.ndis_hdr.ndis_rev);
445                 return (EINVAL);
446         }
447         if (caps.ndis_hdr.ndis_size > caps_len) {
448                 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
449                     "data size %zu\n", caps.ndis_hdr.ndis_size, caps_len);
450                 return (EINVAL);
451         } else if (caps.ndis_hdr.ndis_size < NDIS_RSS_CAPS_SIZE_6_0) {
452                 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
453                     caps.ndis_hdr.ndis_size);
454                 return (EINVAL);
455         }
456
457         /*
458          * Save information for later RSS configuration.
459          */
460         if (caps.ndis_nrxr == 0) {
461                 if_printf(sc->hn_ifp, "0 RX rings!?\n");
462                 return (EINVAL);
463         }
464         if (bootverbose)
465                 if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr);
466         rxr_cnt = caps.ndis_nrxr;
467
468         if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE &&
469             caps.ndis_hdr.ndis_rev >= NDIS_RSS_CAPS_REV_2) {
470                 if (caps.ndis_nind > NDIS_HASH_INDCNT) {
471                         if_printf(sc->hn_ifp,
472                             "too many RSS indirect table entries %u\n",
473                             caps.ndis_nind);
474                         return (EOPNOTSUPP);
475                 }
476                 if (!powerof2(caps.ndis_nind)) {
477                         if_printf(sc->hn_ifp, "RSS indirect table size is not "
478                             "power-of-2 %u\n", caps.ndis_nind);
479                 }
480
481                 if (bootverbose) {
482                         if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
483                             caps.ndis_nind);
484                 }
485                 indsz = caps.ndis_nind;
486         } else {
487                 indsz = NDIS_HASH_INDCNT;
488         }
489         if (indsz < rxr_cnt) {
490                 if_printf(sc->hn_ifp, "# of RX rings (%d) > "
491                     "RSS indirect table size %d\n", rxr_cnt, indsz);
492                 rxr_cnt = indsz;
493         }
494
495         /*
496          * NOTE:
497          * Toeplitz is at the lowest bit, and it is prefered; so ffs(),
498          * instead of fls(), is used here.
499          */
500         hash_fnidx = ffs(caps.ndis_caps & NDIS_RSS_CAP_HASHFUNC_MASK);
501         if (hash_fnidx == 0) {
502                 if_printf(sc->hn_ifp, "no hash functions, caps 0x%08x\n",
503                     caps.ndis_caps);
504                 return (EOPNOTSUPP);
505         }
506         hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */
507
508         if (caps.ndis_caps & NDIS_RSS_CAP_IPV4)
509                 hash_types |= NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4;
510         if (caps.ndis_caps & NDIS_RSS_CAP_IPV6)
511                 hash_types |= NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6;
512         if (caps.ndis_caps & NDIS_RSS_CAP_IPV6_EX)
513                 hash_types |= NDIS_HASH_IPV6_EX | NDIS_HASH_TCP_IPV6_EX;
514         if (hash_types == 0) {
515                 if_printf(sc->hn_ifp, "no hash types, caps 0x%08x\n",
516                     caps.ndis_caps);
517                 return (EOPNOTSUPP);
518         }
519         if (bootverbose)
520                 if_printf(sc->hn_ifp, "RSS caps %#x\n", caps.ndis_caps);
521
522         /* Commit! */
523         sc->hn_rss_ind_size = indsz;
524         sc->hn_rss_hcap = hash_func | hash_types;
525         if (sc->hn_caps & HN_CAP_UDPHASH) {
526                 /* UDP 4-tuple hash is unconditionally enabled. */
527                 sc->hn_rss_hcap |= NDIS_HASH_UDP_IPV4_X;
528         }
529         *rxr_cnt0 = rxr_cnt;
530         return (0);
531 }
532
533 static int
534 hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen)
535 {
536         struct rndis_set_req *req;
537         const struct rndis_set_comp *comp;
538         struct vmbus_xact *xact;
539         size_t reqlen, comp_len;
540         uint32_t rid;
541         int error;
542
543         KASSERT(dlen > 0, ("invalid dlen %zu", dlen));
544
545         reqlen = sizeof(*req) + dlen;
546         xact = vmbus_xact_get(sc->hn_xact, reqlen);
547         if (xact == NULL) {
548                 if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid);
549                 return (ENXIO);
550         }
551         rid = hn_rndis_rid(sc);
552         req = vmbus_xact_req_data(xact);
553         req->rm_type = REMOTE_NDIS_SET_MSG;
554         req->rm_len = reqlen;
555         req->rm_rid = rid;
556         req->rm_oid = oid;
557         req->rm_infobuflen = dlen;
558         req->rm_infobufoffset = RNDIS_SET_REQ_INFOBUFOFFSET;
559         /* Data immediately follows RNDIS set. */
560         memcpy(req + 1, data, dlen);
561
562         comp_len = sizeof(*comp);
563         comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
564             REMOTE_NDIS_SET_CMPLT);
565         if (comp == NULL) {
566                 if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid);
567                 error = EIO;
568                 goto done;
569         }
570
571         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
572                 if_printf(sc->hn_ifp, "RNDIS set 0x%08x failed: "
573                     "status 0x%08x\n", oid, comp->rm_status);
574                 error = EIO;
575                 goto done;
576         }
577         error = 0;
578 done:
579         vmbus_xact_put(xact);
580         return (error);
581 }
582
583 static int
584 hn_rndis_conf_offload(struct hn_softc *sc, int mtu)
585 {
586         struct ndis_offload hwcaps;
587         struct ndis_offload_params params;
588         uint32_t caps = 0;
589         size_t paramsz;
590         int error, tso_maxsz, tso_minsg;
591
592         error = hn_rndis_query_hwcaps(sc, &hwcaps);
593         if (error) {
594                 if_printf(sc->hn_ifp, "hwcaps query failed: %d\n", error);
595                 return (error);
596         }
597
598         /* NOTE: 0 means "no change" */
599         memset(&params, 0, sizeof(params));
600
601         params.ndis_hdr.ndis_type = NDIS_OBJTYPE_DEFAULT;
602         if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30) {
603                 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_2;
604                 paramsz = NDIS_OFFLOAD_PARAMS_SIZE_6_1;
605         } else {
606                 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3;
607                 paramsz = NDIS_OFFLOAD_PARAMS_SIZE;
608         }
609         params.ndis_hdr.ndis_size = paramsz;
610
611         /*
612          * TSO4/TSO6 setup.
613          */
614         tso_maxsz = IP_MAXPACKET;
615         tso_minsg = 2;
616         if (hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
617                 caps |= HN_CAP_TSO4;
618                 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON;
619
620                 if (hwcaps.ndis_lsov2.ndis_ip4_maxsz < tso_maxsz)
621                         tso_maxsz = hwcaps.ndis_lsov2.ndis_ip4_maxsz;
622                 if (hwcaps.ndis_lsov2.ndis_ip4_minsg > tso_minsg)
623                         tso_minsg = hwcaps.ndis_lsov2.ndis_ip4_minsg;
624         }
625         if ((hwcaps.ndis_lsov2.ndis_ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
626             (hwcaps.ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6) ==
627             HN_NDIS_LSOV2_CAP_IP6) {
628                 caps |= HN_CAP_TSO6;
629                 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON;
630
631                 if (hwcaps.ndis_lsov2.ndis_ip6_maxsz < tso_maxsz)
632                         tso_maxsz = hwcaps.ndis_lsov2.ndis_ip6_maxsz;
633                 if (hwcaps.ndis_lsov2.ndis_ip6_minsg > tso_minsg)
634                         tso_minsg = hwcaps.ndis_lsov2.ndis_ip6_minsg;
635         }
636         sc->hn_ndis_tso_szmax = 0;
637         sc->hn_ndis_tso_sgmin = 0;
638         if (caps & (HN_CAP_TSO4 | HN_CAP_TSO6)) {
639                 KASSERT(tso_maxsz <= IP_MAXPACKET,
640                     ("invalid NDIS TSO maxsz %d", tso_maxsz));
641                 KASSERT(tso_minsg >= 2,
642                     ("invalid NDIS TSO minsg %d", tso_minsg));
643                 if (tso_maxsz < tso_minsg * mtu) {
644                         if_printf(sc->hn_ifp, "invalid NDIS TSO config: "
645                             "maxsz %d, minsg %d, mtu %d; "
646                             "disable TSO4 and TSO6\n",
647                             tso_maxsz, tso_minsg, mtu);
648                         caps &= ~(HN_CAP_TSO4 | HN_CAP_TSO6);
649                         params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_OFF;
650                         params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_OFF;
651                 } else {
652                         sc->hn_ndis_tso_szmax = tso_maxsz;
653                         sc->hn_ndis_tso_sgmin = tso_minsg;
654                         if (bootverbose) {
655                                 if_printf(sc->hn_ifp, "NDIS TSO "
656                                     "szmax %d sgmin %d\n",
657                                     sc->hn_ndis_tso_szmax,
658                                     sc->hn_ndis_tso_sgmin);
659                         }
660                 }
661         }
662
663         /* IPv4 checksum */
664         if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4) ==
665             HN_NDIS_TXCSUM_CAP_IP4) {
666                 caps |= HN_CAP_IPCS;
667                 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TX;
668         }
669         if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4) {
670                 if (params.ndis_ip4csum == NDIS_OFFLOAD_PARAM_TX)
671                         params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX;
672                 else
673                         params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_RX;
674         }
675
676         /* TCP4 checksum */
677         if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_TCP4) ==
678             HN_NDIS_TXCSUM_CAP_TCP4) {
679                 caps |= HN_CAP_TCP4CS;
680                 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TX;
681         }
682         if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) {
683                 if (params.ndis_tcp4csum == NDIS_OFFLOAD_PARAM_TX)
684                         params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX;
685                 else
686                         params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_RX;
687         }
688
689         /* UDP4 checksum */
690         if (hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
691                 caps |= HN_CAP_UDP4CS;
692                 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TX;
693         }
694         if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) {
695                 if (params.ndis_udp4csum == NDIS_OFFLOAD_PARAM_TX)
696                         params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX;
697                 else
698                         params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_RX;
699         }
700
701         /* TCP6 checksum */
702         if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_TCP6) ==
703             HN_NDIS_TXCSUM_CAP_TCP6) {
704                 caps |= HN_CAP_TCP6CS;
705                 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TX;
706         }
707         if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6) {
708                 if (params.ndis_tcp6csum == NDIS_OFFLOAD_PARAM_TX)
709                         params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX;
710                 else
711                         params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_RX;
712         }
713
714         /* UDP6 checksum */
715         if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_UDP6) ==
716             HN_NDIS_TXCSUM_CAP_UDP6) {
717                 caps |= HN_CAP_UDP6CS;
718                 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TX;
719         }
720         if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6) {
721                 if (params.ndis_udp6csum == NDIS_OFFLOAD_PARAM_TX)
722                         params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX;
723                 else
724                         params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_RX;
725         }
726
727         if (bootverbose) {
728                 if_printf(sc->hn_ifp, "offload csum: "
729                     "ip4 %u, tcp4 %u, udp4 %u, tcp6 %u, udp6 %u\n",
730                     params.ndis_ip4csum,
731                     params.ndis_tcp4csum,
732                     params.ndis_udp4csum,
733                     params.ndis_tcp6csum,
734                     params.ndis_udp6csum);
735                 if_printf(sc->hn_ifp, "offload lsov2: ip4 %u, ip6 %u\n",
736                     params.ndis_lsov2_ip4,
737                     params.ndis_lsov2_ip6);
738         }
739
740         error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, &params, paramsz);
741         if (error) {
742                 if_printf(sc->hn_ifp, "offload config failed: %d\n", error);
743                 return (error);
744         }
745
746         if (bootverbose)
747                 if_printf(sc->hn_ifp, "offload config done\n");
748         sc->hn_caps |= caps;
749         return (0);
750 }
751
752 int
753 hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags)
754 {
755         struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
756         struct ndis_rss_params *prm = &rss->rss_params;
757         int error, rss_size;
758
759         /*
760          * Only NDIS 6.20+ is supported:
761          * We only support 4bytes element in indirect table, which has been
762          * adopted since NDIS 6.20.
763          */
764         KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_20,
765             ("NDIS 6.20+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
766
767         /* XXX only one can be specified through, popcnt? */
768         KASSERT((sc->hn_rss_hash & NDIS_HASH_FUNCTION_MASK),
769             ("no hash func %08x", sc->hn_rss_hash));
770         KASSERT((sc->hn_rss_hash & NDIS_HASH_STD),
771             ("no standard hash types %08x", sc->hn_rss_hash));
772         KASSERT(sc->hn_rss_ind_size > 0, ("no indirect table size"));
773
774         if (bootverbose) {
775                 if_printf(sc->hn_ifp, "RSS indirect table size %d, "
776                     "hash 0x%08x\n", sc->hn_rss_ind_size, sc->hn_rss_hash);
777         }
778
779         /*
780          * NOTE:
781          * DO NOT whack rss_key and rss_ind, which are setup by the caller.
782          */
783         memset(prm, 0, sizeof(*prm));
784         rss_size = NDIS_RSSPRM_TOEPLITZ_SIZE(sc->hn_rss_ind_size);
785
786         prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS;
787         prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2;
788         prm->ndis_hdr.ndis_size = rss_size;
789         prm->ndis_flags = flags;
790         prm->ndis_hash = sc->hn_rss_hash &
791             (NDIS_HASH_FUNCTION_MASK | NDIS_HASH_STD);
792         prm->ndis_indsize = sizeof(rss->rss_ind[0]) * sc->hn_rss_ind_size;
793         prm->ndis_indoffset =
794             __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]);
795         prm->ndis_keysize = sizeof(rss->rss_key);
796         prm->ndis_keyoffset =
797             __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]);
798
799         error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS,
800             rss, rss_size);
801         if (error) {
802                 if_printf(sc->hn_ifp, "RSS config failed: %d\n", error);
803         } else {
804                 if (bootverbose)
805                         if_printf(sc->hn_ifp, "RSS config done\n");
806         }
807         return (error);
808 }
809
810 int
811 hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter)
812 {
813         int error;
814
815         error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER,
816             &filter, sizeof(filter));
817         if (error) {
818                 if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n",
819                     filter, error);
820         } else {
821                 if (bootverbose) {
822                         if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n",
823                             filter);
824                 }
825         }
826         return (error);
827 }
828
829 static int
830 hn_rndis_init(struct hn_softc *sc)
831 {
832         struct rndis_init_req *req;
833         const struct rndis_init_comp *comp;
834         struct vmbus_xact *xact;
835         size_t comp_len;
836         uint32_t rid;
837         int error;
838
839         xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
840         if (xact == NULL) {
841                 if_printf(sc->hn_ifp, "no xact for RNDIS init\n");
842                 return (ENXIO);
843         }
844         rid = hn_rndis_rid(sc);
845         req = vmbus_xact_req_data(xact);
846         req->rm_type = REMOTE_NDIS_INITIALIZE_MSG;
847         req->rm_len = sizeof(*req);
848         req->rm_rid = rid;
849         req->rm_ver_major = RNDIS_VERSION_MAJOR;
850         req->rm_ver_minor = RNDIS_VERSION_MINOR;
851         req->rm_max_xfersz = HN_RNDIS_XFER_SIZE;
852
853         comp_len = RNDIS_INIT_COMP_SIZE_MIN;
854         comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len,
855             REMOTE_NDIS_INITIALIZE_CMPLT);
856         if (comp == NULL) {
857                 if_printf(sc->hn_ifp, "exec RNDIS init failed\n");
858                 error = EIO;
859                 goto done;
860         }
861
862         if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
863                 if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n",
864                     comp->rm_status);
865                 error = EIO;
866                 goto done;
867         }
868         sc->hn_rndis_agg_size = comp->rm_pktmaxsz;
869         sc->hn_rndis_agg_pkts = comp->rm_pktmaxcnt;
870         sc->hn_rndis_agg_align = 1U << comp->rm_align;
871
872         if (sc->hn_rndis_agg_align < sizeof(uint32_t)) {
873                 /*
874                  * The RNDIS packet messsage encap assumes that the RNDIS
875                  * packet message is at least 4 bytes aligned.  Fix up the
876                  * alignment here, if the remote side sets the alignment
877                  * too low.
878                  */
879                 if_printf(sc->hn_ifp, "fixup RNDIS aggpkt align: %u -> %zu\n",
880                     sc->hn_rndis_agg_align, sizeof(uint32_t));
881                 sc->hn_rndis_agg_align = sizeof(uint32_t);
882         }
883
884         if (bootverbose) {
885                 if_printf(sc->hn_ifp, "RNDIS ver %u.%u, "
886                     "aggpkt size %u, aggpkt cnt %u, aggpkt align %u\n",
887                     comp->rm_ver_major, comp->rm_ver_minor,
888                     sc->hn_rndis_agg_size, sc->hn_rndis_agg_pkts,
889                     sc->hn_rndis_agg_align);
890         }
891         error = 0;
892 done:
893         vmbus_xact_put(xact);
894         return (error);
895 }
896
897 static int
898 hn_rndis_halt(struct hn_softc *sc)
899 {
900         struct vmbus_xact *xact;
901         struct rndis_halt_req *halt;
902         struct hn_nvs_sendctx sndc;
903         size_t comp_len;
904
905         xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt));
906         if (xact == NULL) {
907                 if_printf(sc->hn_ifp, "no xact for RNDIS halt\n");
908                 return (ENXIO);
909         }
910         halt = vmbus_xact_req_data(xact);
911         halt->rm_type = REMOTE_NDIS_HALT_MSG;
912         halt->rm_len = sizeof(*halt);
913         halt->rm_rid = hn_rndis_rid(sc);
914
915         /* No RNDIS completion; rely on NVS message send completion */
916         hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact);
917         hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len);
918
919         vmbus_xact_put(xact);
920         if (bootverbose)
921                 if_printf(sc->hn_ifp, "RNDIS halt done\n");
922         return (0);
923 }
924
925 static int
926 hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps)
927 {
928         struct ndis_offload in;
929         size_t caps_len, size;
930         int error;
931
932         memset(&in, 0, sizeof(in));
933         in.ndis_hdr.ndis_type = NDIS_OBJTYPE_OFFLOAD;
934         if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) {
935                 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_3;
936                 size = NDIS_OFFLOAD_SIZE;
937         } else if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_1) {
938                 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_2;
939                 size = NDIS_OFFLOAD_SIZE_6_1;
940         } else {
941                 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1;
942                 size = NDIS_OFFLOAD_SIZE_6_0;
943         }
944         in.ndis_hdr.ndis_size = size;
945
946         caps_len = NDIS_OFFLOAD_SIZE;
947         error = hn_rndis_query2(sc, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
948             &in, size, caps, &caps_len, NDIS_OFFLOAD_SIZE_6_0);
949         if (error)
950                 return (error);
951
952         /*
953          * Preliminary verification.
954          */
955         if (caps->ndis_hdr.ndis_type != NDIS_OBJTYPE_OFFLOAD) {
956                 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
957                     caps->ndis_hdr.ndis_type);
958                 return (EINVAL);
959         }
960         if (caps->ndis_hdr.ndis_rev < NDIS_OFFLOAD_REV_1) {
961                 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
962                     caps->ndis_hdr.ndis_rev);
963                 return (EINVAL);
964         }
965         if (caps->ndis_hdr.ndis_size > caps_len) {
966                 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
967                     "data size %zu\n", caps->ndis_hdr.ndis_size, caps_len);
968                 return (EINVAL);
969         } else if (caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE_6_0) {
970                 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
971                     caps->ndis_hdr.ndis_size);
972                 return (EINVAL);
973         }
974
975         if (bootverbose) {
976                 /*
977                  * NOTE:
978                  * caps->ndis_hdr.ndis_size MUST be checked before accessing
979                  * NDIS 6.1+ specific fields.
980                  */
981                 if_printf(sc->hn_ifp, "hwcaps rev %u\n",
982                     caps->ndis_hdr.ndis_rev);
983
984                 if_printf(sc->hn_ifp, "hwcaps csum: "
985                     "ip4 tx 0x%x/0x%x rx 0x%x/0x%x, "
986                     "ip6 tx 0x%x/0x%x rx 0x%x/0x%x\n",
987                     caps->ndis_csum.ndis_ip4_txcsum,
988                     caps->ndis_csum.ndis_ip4_txenc,
989                     caps->ndis_csum.ndis_ip4_rxcsum,
990                     caps->ndis_csum.ndis_ip4_rxenc,
991                     caps->ndis_csum.ndis_ip6_txcsum,
992                     caps->ndis_csum.ndis_ip6_txenc,
993                     caps->ndis_csum.ndis_ip6_rxcsum,
994                     caps->ndis_csum.ndis_ip6_rxenc);
995                 if_printf(sc->hn_ifp, "hwcaps lsov2: "
996                     "ip4 maxsz %u minsg %u encap 0x%x, "
997                     "ip6 maxsz %u minsg %u encap 0x%x opts 0x%x\n",
998                     caps->ndis_lsov2.ndis_ip4_maxsz,
999                     caps->ndis_lsov2.ndis_ip4_minsg,
1000                     caps->ndis_lsov2.ndis_ip4_encap,
1001                     caps->ndis_lsov2.ndis_ip6_maxsz,
1002                     caps->ndis_lsov2.ndis_ip6_minsg,
1003                     caps->ndis_lsov2.ndis_ip6_encap,
1004                     caps->ndis_lsov2.ndis_ip6_opts);
1005         }
1006         return (0);
1007 }
1008
1009 int
1010 hn_rndis_attach(struct hn_softc *sc, int mtu, int *init_done)
1011 {
1012         int error;
1013
1014         *init_done = 0;
1015
1016         /*
1017          * Initialize RNDIS.
1018          */
1019         error = hn_rndis_init(sc);
1020         if (error)
1021                 return (error);
1022         *init_done = 1;
1023
1024         /*
1025          * Configure NDIS offload settings.
1026          */
1027         hn_rndis_conf_offload(sc, mtu);
1028         return (0);
1029 }
1030
1031 void
1032 hn_rndis_detach(struct hn_softc *sc)
1033 {
1034
1035         /* Halt the RNDIS. */
1036         hn_rndis_halt(sc);
1037 }