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