xref: /freebsd/sys/dev/hyperv/netvsc/hn_rndis.c (revision f391d6bc1d0464f62f1b8264666c897a680156b1)
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_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 	if (HN_CAN_SLEEP(sc))
236 		return (vmbus_xact_wait(xact, comp_len));
237 	else
238 		return (vmbus_xact_busywait(xact, comp_len));
239 }
240 
241 static const void *
242 hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid,
243     size_t reqlen, size_t *comp_len0, uint32_t comp_type)
244 {
245 	const struct rndis_comp_hdr *comp;
246 	size_t comp_len, min_complen = *comp_len0;
247 
248 	KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid));
249 	KASSERT(min_complen >= sizeof(*comp),
250 	    ("invalid minimum complete len %zu", min_complen));
251 
252 	/*
253 	 * Execute the xact setup by the caller.
254 	 */
255 	comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_nvs_sendctx_none,
256 	    &comp_len);
257 	if (comp == NULL)
258 		return (NULL);
259 
260 	/*
261 	 * Check this RNDIS complete message.
262 	 */
263 	if (comp_len < min_complen) {
264 		if (comp_len >= sizeof(*comp)) {
265 			/* rm_status field is valid */
266 			if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, "
267 			    "status 0x%08x\n", comp_len, comp->rm_status);
268 		} else {
269 			if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n",
270 			    comp_len);
271 		}
272 		return (NULL);
273 	}
274 	if (comp->rm_len < min_complen) {
275 		if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n",
276 		    comp->rm_len);
277 		return (NULL);
278 	}
279 	if (comp->rm_type != comp_type) {
280 		if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, "
281 		    "expect 0x%08x\n", comp->rm_type, comp_type);
282 		return (NULL);
283 	}
284 	if (comp->rm_rid != rid) {
285 		if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, "
286 		    "expect %u\n", comp->rm_rid, rid);
287 		return (NULL);
288 	}
289 	/* All pass! */
290 	*comp_len0 = comp_len;
291 	return (comp);
292 }
293 
294 static int
295 hn_rndis_query(struct hn_softc *sc, uint32_t oid,
296     const void *idata, size_t idlen, void *odata, size_t *odlen0)
297 {
298 
299 	return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0));
300 }
301 
302 static int
303 hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
304     const void *idata, size_t idlen, void *odata, size_t *odlen0,
305     size_t min_odlen)
306 {
307 	struct rndis_query_req *req;
308 	const struct rndis_query_comp *comp;
309 	struct vmbus_xact *xact;
310 	size_t reqlen, odlen = *odlen0, comp_len;
311 	int error, ofs;
312 	uint32_t rid;
313 
314 	reqlen = sizeof(*req) + idlen;
315 	xact = vmbus_xact_get(sc->hn_xact, reqlen);
316 	if (xact == NULL) {
317 		if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid);
318 		return (ENXIO);
319 	}
320 	rid = hn_rndis_rid(sc);
321 	req = vmbus_xact_req_data(xact);
322 	req->rm_type = REMOTE_NDIS_QUERY_MSG;
323 	req->rm_len = reqlen;
324 	req->rm_rid = rid;
325 	req->rm_oid = oid;
326 	/*
327 	 * XXX
328 	 * This is _not_ RNDIS Spec conforming:
329 	 * "This MUST be set to 0 when there is no input data
330 	 *  associated with the OID."
331 	 *
332 	 * If this field was set to 0 according to the RNDIS Spec,
333 	 * Hyper-V would set non-SUCCESS status in the query
334 	 * completion.
335 	 */
336 	req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET;
337 
338 	if (idlen > 0) {
339 		req->rm_infobuflen = idlen;
340 		/* Input data immediately follows RNDIS query. */
341 		memcpy(req + 1, idata, idlen);
342 	}
343 
344 	comp_len = sizeof(*comp) + min_odlen;
345 	comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
346 	    REMOTE_NDIS_QUERY_CMPLT);
347 	if (comp == NULL) {
348 		if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid);
349 		error = EIO;
350 		goto done;
351 	}
352 
353 	if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
354 		if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: "
355 		    "status 0x%08x\n", oid, comp->rm_status);
356 		error = EIO;
357 		goto done;
358 	}
359 	if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) {
360 		/* No output data! */
361 		if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid);
362 		*odlen0 = 0;
363 		error = 0;
364 		goto done;
365 	}
366 
367 	/*
368 	 * Check output data length and offset.
369 	 */
370 	/* ofs is the offset from the beginning of comp. */
371 	ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset);
372 	if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
373 		if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
374 		    "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);
375 		error = EINVAL;
376 		goto done;
377 	}
378 
379 	/*
380 	 * Save output data.
381 	 */
382 	if (comp->rm_infobuflen < odlen)
383 		odlen = comp->rm_infobuflen;
384 	memcpy(odata, ((const uint8_t *)comp) + ofs, odlen);
385 	*odlen0 = odlen;
386 
387 	error = 0;
388 done:
389 	vmbus_xact_put(xact);
390 	return (error);
391 }
392 
393 int
394 hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0)
395 {
396 	struct ndis_rss_caps in, caps;
397 	size_t caps_len;
398 	int error, indsz, rxr_cnt, hash_fnidx;
399 	uint32_t hash_func = 0, hash_types = 0;
400 
401 	*rxr_cnt0 = 0;
402 
403 	if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20)
404 		return (EOPNOTSUPP);
405 
406 	memset(&in, 0, sizeof(in));
407 	in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS;
408 	in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
409 	in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
410 
411 	caps_len = NDIS_RSS_CAPS_SIZE;
412 	error = hn_rndis_query2(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
413 	    &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len, NDIS_RSS_CAPS_SIZE_6_0);
414 	if (error)
415 		return (error);
416 
417 	/*
418 	 * Preliminary verification.
419 	 */
420 	if (caps.ndis_hdr.ndis_type != NDIS_OBJTYPE_RSS_CAPS) {
421 		if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
422 		    caps.ndis_hdr.ndis_type);
423 		return (EINVAL);
424 	}
425 	if (caps.ndis_hdr.ndis_rev < NDIS_RSS_CAPS_REV_1) {
426 		if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
427 		    caps.ndis_hdr.ndis_rev);
428 		return (EINVAL);
429 	}
430 	if (caps.ndis_hdr.ndis_size > caps_len) {
431 		if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
432 		    "data size %zu\n", caps.ndis_hdr.ndis_size, caps_len);
433 		return (EINVAL);
434 	} else if (caps.ndis_hdr.ndis_size < NDIS_RSS_CAPS_SIZE_6_0) {
435 		if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
436 		    caps.ndis_hdr.ndis_size);
437 		return (EINVAL);
438 	}
439 
440 	/*
441 	 * Save information for later RSS configuration.
442 	 */
443 	if (caps.ndis_nrxr == 0) {
444 		if_printf(sc->hn_ifp, "0 RX rings!?\n");
445 		return (EINVAL);
446 	}
447 	if (bootverbose)
448 		if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr);
449 	rxr_cnt = caps.ndis_nrxr;
450 
451 	if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE &&
452 	    caps.ndis_hdr.ndis_rev >= NDIS_RSS_CAPS_REV_2) {
453 		if (caps.ndis_nind > NDIS_HASH_INDCNT) {
454 			if_printf(sc->hn_ifp,
455 			    "too many RSS indirect table entries %u\n",
456 			    caps.ndis_nind);
457 			return (EOPNOTSUPP);
458 		}
459 		if (!powerof2(caps.ndis_nind)) {
460 			if_printf(sc->hn_ifp, "RSS indirect table size is not "
461 			    "power-of-2 %u\n", caps.ndis_nind);
462 		}
463 
464 		if (bootverbose) {
465 			if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
466 			    caps.ndis_nind);
467 		}
468 		indsz = caps.ndis_nind;
469 	} else {
470 		indsz = NDIS_HASH_INDCNT;
471 	}
472 	if (indsz < rxr_cnt) {
473 		if_printf(sc->hn_ifp, "# of RX rings (%d) > "
474 		    "RSS indirect table size %d\n", rxr_cnt, indsz);
475 		rxr_cnt = indsz;
476 	}
477 
478 	/*
479 	 * NOTE:
480 	 * Toeplitz is at the lowest bit, and it is prefered; so ffs(),
481 	 * instead of fls(), is used here.
482 	 */
483 	hash_fnidx = ffs(caps.ndis_caps & NDIS_RSS_CAP_HASHFUNC_MASK);
484 	if (hash_fnidx == 0) {
485 		if_printf(sc->hn_ifp, "no hash functions, caps 0x%08x\n",
486 		    caps.ndis_caps);
487 		return (EOPNOTSUPP);
488 	}
489 	hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */
490 
491 	if (caps.ndis_caps & NDIS_RSS_CAP_IPV4)
492 		hash_types |= NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4;
493 	if (caps.ndis_caps & NDIS_RSS_CAP_IPV6)
494 		hash_types |= NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6;
495 	if (caps.ndis_caps & NDIS_RSS_CAP_IPV6_EX)
496 		hash_types |= NDIS_HASH_IPV6_EX | NDIS_HASH_TCP_IPV6_EX;
497 	if (hash_types == 0) {
498 		if_printf(sc->hn_ifp, "no hash types, caps 0x%08x\n",
499 		    caps.ndis_caps);
500 		return (EOPNOTSUPP);
501 	}
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 (bootverbose) {
847 		if_printf(sc->hn_ifp, "RNDIS ver %u.%u, pktsz %u, pktcnt %u, "
848 		    "align %u\n", comp->rm_ver_major, comp->rm_ver_minor,
849 		    sc->hn_rndis_agg_size, sc->hn_rndis_agg_pkts,
850 		    sc->hn_rndis_agg_align);
851 	}
852 	error = 0;
853 done:
854 	vmbus_xact_put(xact);
855 	return (error);
856 }
857 
858 static int
859 hn_rndis_halt(struct hn_softc *sc)
860 {
861 	struct vmbus_xact *xact;
862 	struct rndis_halt_req *halt;
863 	struct hn_nvs_sendctx sndc;
864 	size_t comp_len;
865 
866 	xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt));
867 	if (xact == NULL) {
868 		if_printf(sc->hn_ifp, "no xact for RNDIS halt\n");
869 		return (ENXIO);
870 	}
871 	halt = vmbus_xact_req_data(xact);
872 	halt->rm_type = REMOTE_NDIS_HALT_MSG;
873 	halt->rm_len = sizeof(*halt);
874 	halt->rm_rid = hn_rndis_rid(sc);
875 
876 	/* No RNDIS completion; rely on NVS message send completion */
877 	hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact);
878 	hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len);
879 
880 	vmbus_xact_put(xact);
881 	if (bootverbose)
882 		if_printf(sc->hn_ifp, "RNDIS halt done\n");
883 	return (0);
884 }
885 
886 static int
887 hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps)
888 {
889 	struct ndis_offload in;
890 	size_t caps_len, size;
891 	int error;
892 
893 	memset(&in, 0, sizeof(in));
894 	in.ndis_hdr.ndis_type = NDIS_OBJTYPE_OFFLOAD;
895 	if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) {
896 		in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_3;
897 		size = NDIS_OFFLOAD_SIZE;
898 	} else if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_1) {
899 		in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_2;
900 		size = NDIS_OFFLOAD_SIZE_6_1;
901 	} else {
902 		in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1;
903 		size = NDIS_OFFLOAD_SIZE_6_0;
904 	}
905 	in.ndis_hdr.ndis_size = size;
906 
907 	caps_len = NDIS_OFFLOAD_SIZE;
908 	error = hn_rndis_query2(sc, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
909 	    &in, size, caps, &caps_len, NDIS_OFFLOAD_SIZE_6_0);
910 	if (error)
911 		return (error);
912 
913 	/*
914 	 * Preliminary verification.
915 	 */
916 	if (caps->ndis_hdr.ndis_type != NDIS_OBJTYPE_OFFLOAD) {
917 		if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
918 		    caps->ndis_hdr.ndis_type);
919 		return (EINVAL);
920 	}
921 	if (caps->ndis_hdr.ndis_rev < NDIS_OFFLOAD_REV_1) {
922 		if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
923 		    caps->ndis_hdr.ndis_rev);
924 		return (EINVAL);
925 	}
926 	if (caps->ndis_hdr.ndis_size > caps_len) {
927 		if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
928 		    "data size %zu\n", caps->ndis_hdr.ndis_size, caps_len);
929 		return (EINVAL);
930 	} else if (caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE_6_0) {
931 		if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
932 		    caps->ndis_hdr.ndis_size);
933 		return (EINVAL);
934 	}
935 
936 	if (bootverbose) {
937 		/*
938 		 * NOTE:
939 		 * caps->ndis_hdr.ndis_size MUST be checked before accessing
940 		 * NDIS 6.1+ specific fields.
941 		 */
942 		if_printf(sc->hn_ifp, "hwcaps rev %u\n",
943 		    caps->ndis_hdr.ndis_rev);
944 
945 		if_printf(sc->hn_ifp, "hwcaps csum: "
946 		    "ip4 tx 0x%x/0x%x rx 0x%x/0x%x, "
947 		    "ip6 tx 0x%x/0x%x rx 0x%x/0x%x\n",
948 		    caps->ndis_csum.ndis_ip4_txcsum,
949 		    caps->ndis_csum.ndis_ip4_txenc,
950 		    caps->ndis_csum.ndis_ip4_rxcsum,
951 		    caps->ndis_csum.ndis_ip4_rxenc,
952 		    caps->ndis_csum.ndis_ip6_txcsum,
953 		    caps->ndis_csum.ndis_ip6_txenc,
954 		    caps->ndis_csum.ndis_ip6_rxcsum,
955 		    caps->ndis_csum.ndis_ip6_rxenc);
956 		if_printf(sc->hn_ifp, "hwcaps lsov2: "
957 		    "ip4 maxsz %u minsg %u encap 0x%x, "
958 		    "ip6 maxsz %u minsg %u encap 0x%x opts 0x%x\n",
959 		    caps->ndis_lsov2.ndis_ip4_maxsz,
960 		    caps->ndis_lsov2.ndis_ip4_minsg,
961 		    caps->ndis_lsov2.ndis_ip4_encap,
962 		    caps->ndis_lsov2.ndis_ip6_maxsz,
963 		    caps->ndis_lsov2.ndis_ip6_minsg,
964 		    caps->ndis_lsov2.ndis_ip6_encap,
965 		    caps->ndis_lsov2.ndis_ip6_opts);
966 	}
967 	return (0);
968 }
969 
970 int
971 hn_rndis_attach(struct hn_softc *sc, int mtu)
972 {
973 	int error;
974 
975 	/*
976 	 * Initialize RNDIS.
977 	 */
978 	error = hn_rndis_init(sc);
979 	if (error)
980 		return (error);
981 
982 	/*
983 	 * Configure NDIS offload settings.
984 	 * XXX no offloading, if error happened?
985 	 */
986 	hn_rndis_conf_offload(sc, mtu);
987 	return (0);
988 }
989 
990 void
991 hn_rndis_detach(struct hn_softc *sc)
992 {
993 
994 	/* Halt the RNDIS. */
995 	hn_rndis_halt(sc);
996 }
997