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