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