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