hn_rndis.c revision 324474
1/*-
2 * Copyright (c) 2009-2012,2016-2017 Microsoft Corp.
3 * Copyright (c) 2010-2012 Citrix Inc.
4 * Copyright (c) 2012 NetApp Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice unmodified, this list of conditions, and the following
12 *    disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/11/sys/dev/hyperv/netvsc/hn_rndis.c 324474 2017-10-10 05:38:07Z sephe $");
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
80static const void	*hn_rndis_xact_exec1(struct hn_softc *,
81			    struct vmbus_xact *, size_t,
82			    struct hn_nvs_sendctx *, size_t *);
83static const void	*hn_rndis_xact_execute(struct hn_softc *,
84			    struct vmbus_xact *, uint32_t, size_t, size_t *,
85			    uint32_t);
86static int		hn_rndis_query(struct hn_softc *, uint32_t,
87			    const void *, size_t, void *, size_t *);
88static int		hn_rndis_query2(struct hn_softc *, uint32_t,
89			    const void *, size_t, void *, size_t *, size_t);
90static int		hn_rndis_set(struct hn_softc *, uint32_t,
91			    const void *, size_t);
92static int		hn_rndis_init(struct hn_softc *);
93static int		hn_rndis_halt(struct hn_softc *);
94static int		hn_rndis_conf_offload(struct hn_softc *, int);
95static int		hn_rndis_query_hwcaps(struct hn_softc *,
96			    struct ndis_offload *);
97
98static __inline uint32_t
99hn_rndis_rid(struct hn_softc *sc)
100{
101	uint32_t rid;
102
103again:
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
112void
113hn_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
155int
156hn_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
173int
174hn_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
191static const void *
192hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen,
193    struct hn_nvs_sendctx *sndc, size_t *comp_len)
194{
195	struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT];
196	int gpa_cnt, error;
197	bus_addr_t paddr;
198
199	KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0,
200	    ("invalid request length %zu", reqlen));
201
202	/*
203	 * Setup the SG list.
204	 */
205	paddr = vmbus_xact_req_paddr(xact);
206	KASSERT((paddr & PAGE_MASK) == 0,
207	    ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr));
208	for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) {
209		int len = PAGE_SIZE;
210
211		if (reqlen == 0)
212			break;
213		if (reqlen < len)
214			len = reqlen;
215
216		gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt;
217		gpa[gpa_cnt].gpa_len = len;
218		gpa[gpa_cnt].gpa_ofs = 0;
219
220		reqlen -= len;
221	}
222	KASSERT(reqlen == 0, ("still have %zu request data left", reqlen));
223
224	/*
225	 * Send this RNDIS control message and wait for its completion
226	 * message.
227	 */
228	vmbus_xact_activate(xact);
229	error = hn_nvs_send_rndis_ctrl(sc->hn_prichan, sndc, gpa, gpa_cnt);
230	if (error) {
231		vmbus_xact_deactivate(xact);
232		if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error);
233		return (NULL);
234	}
235	return (vmbus_chan_xact_wait(sc->hn_prichan, xact, comp_len,
236	    HN_CAN_SLEEP(sc)));
237}
238
239static const void *
240hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid,
241    size_t reqlen, size_t *comp_len0, uint32_t comp_type)
242{
243	const struct rndis_comp_hdr *comp;
244	size_t comp_len, min_complen = *comp_len0;
245
246	KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid));
247	KASSERT(min_complen >= sizeof(*comp),
248	    ("invalid minimum complete len %zu", min_complen));
249
250	/*
251	 * Execute the xact setup by the caller.
252	 */
253	comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_nvs_sendctx_none,
254	    &comp_len);
255	if (comp == NULL)
256		return (NULL);
257
258	/*
259	 * Check this RNDIS complete message.
260	 */
261	if (comp_len < min_complen) {
262		if (comp_len >= sizeof(*comp)) {
263			/* rm_status field is valid */
264			if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, "
265			    "status 0x%08x\n", comp_len, comp->rm_status);
266		} else {
267			if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n",
268			    comp_len);
269		}
270		return (NULL);
271	}
272	if (comp->rm_len < min_complen) {
273		if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n",
274		    comp->rm_len);
275		return (NULL);
276	}
277	if (comp->rm_type != comp_type) {
278		if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, "
279		    "expect 0x%08x\n", comp->rm_type, comp_type);
280		return (NULL);
281	}
282	if (comp->rm_rid != rid) {
283		if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, "
284		    "expect %u\n", comp->rm_rid, rid);
285		return (NULL);
286	}
287	/* All pass! */
288	*comp_len0 = comp_len;
289	return (comp);
290}
291
292static int
293hn_rndis_query(struct hn_softc *sc, uint32_t oid,
294    const void *idata, size_t idlen, void *odata, size_t *odlen0)
295{
296
297	return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0));
298}
299
300static int
301hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
302    const void *idata, size_t idlen, void *odata, size_t *odlen0,
303    size_t min_odlen)
304{
305	struct rndis_query_req *req;
306	const struct rndis_query_comp *comp;
307	struct vmbus_xact *xact;
308	size_t reqlen, odlen = *odlen0, comp_len;
309	int error, ofs;
310	uint32_t rid;
311
312	reqlen = sizeof(*req) + idlen;
313	xact = vmbus_xact_get(sc->hn_xact, reqlen);
314	if (xact == NULL) {
315		if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid);
316		return (ENXIO);
317	}
318	rid = hn_rndis_rid(sc);
319	req = vmbus_xact_req_data(xact);
320	req->rm_type = REMOTE_NDIS_QUERY_MSG;
321	req->rm_len = reqlen;
322	req->rm_rid = rid;
323	req->rm_oid = oid;
324	/*
325	 * XXX
326	 * This is _not_ RNDIS Spec conforming:
327	 * "This MUST be set to 0 when there is no input data
328	 *  associated with the OID."
329	 *
330	 * If this field was set to 0 according to the RNDIS Spec,
331	 * Hyper-V would set non-SUCCESS status in the query
332	 * completion.
333	 */
334	req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET;
335
336	if (idlen > 0) {
337		req->rm_infobuflen = idlen;
338		/* Input data immediately follows RNDIS query. */
339		memcpy(req + 1, idata, idlen);
340	}
341
342	comp_len = sizeof(*comp) + min_odlen;
343	comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
344	    REMOTE_NDIS_QUERY_CMPLT);
345	if (comp == NULL) {
346		if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid);
347		error = EIO;
348		goto done;
349	}
350
351	if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
352		if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: "
353		    "status 0x%08x\n", oid, comp->rm_status);
354		error = EIO;
355		goto done;
356	}
357	if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) {
358		/* No output data! */
359		if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid);
360		*odlen0 = 0;
361		error = 0;
362		goto done;
363	}
364
365	/*
366	 * Check output data length and offset.
367	 */
368	/* ofs is the offset from the beginning of comp. */
369	ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset);
370	if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
371		if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
372		    "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);
373		error = EINVAL;
374		goto done;
375	}
376
377	/*
378	 * Save output data.
379	 */
380	if (comp->rm_infobuflen < odlen)
381		odlen = comp->rm_infobuflen;
382	memcpy(odata, ((const uint8_t *)comp) + ofs, odlen);
383	*odlen0 = odlen;
384
385	error = 0;
386done:
387	vmbus_xact_put(xact);
388	return (error);
389}
390
391int
392hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0)
393{
394	struct ndis_rss_caps in, caps;
395	size_t caps_len;
396	int error, indsz, rxr_cnt, hash_fnidx;
397	uint32_t hash_func = 0, hash_types = 0;
398
399	*rxr_cnt0 = 0;
400
401	if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20)
402		return (EOPNOTSUPP);
403
404	memset(&in, 0, sizeof(in));
405	in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS;
406	in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
407	in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
408
409	caps_len = NDIS_RSS_CAPS_SIZE;
410	error = hn_rndis_query2(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
411	    &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len, NDIS_RSS_CAPS_SIZE_6_0);
412	if (error)
413		return (error);
414
415	/*
416	 * Preliminary verification.
417	 */
418	if (caps.ndis_hdr.ndis_type != NDIS_OBJTYPE_RSS_CAPS) {
419		if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
420		    caps.ndis_hdr.ndis_type);
421		return (EINVAL);
422	}
423	if (caps.ndis_hdr.ndis_rev < NDIS_RSS_CAPS_REV_1) {
424		if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
425		    caps.ndis_hdr.ndis_rev);
426		return (EINVAL);
427	}
428	if (caps.ndis_hdr.ndis_size > caps_len) {
429		if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
430		    "data size %zu\n", caps.ndis_hdr.ndis_size, caps_len);
431		return (EINVAL);
432	} else if (caps.ndis_hdr.ndis_size < NDIS_RSS_CAPS_SIZE_6_0) {
433		if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
434		    caps.ndis_hdr.ndis_size);
435		return (EINVAL);
436	}
437
438	/*
439	 * Save information for later RSS configuration.
440	 */
441	if (caps.ndis_nrxr == 0) {
442		if_printf(sc->hn_ifp, "0 RX rings!?\n");
443		return (EINVAL);
444	}
445	if (bootverbose)
446		if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr);
447	rxr_cnt = caps.ndis_nrxr;
448
449	if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE &&
450	    caps.ndis_hdr.ndis_rev >= NDIS_RSS_CAPS_REV_2) {
451		if (caps.ndis_nind > NDIS_HASH_INDCNT) {
452			if_printf(sc->hn_ifp,
453			    "too many RSS indirect table entries %u\n",
454			    caps.ndis_nind);
455			return (EOPNOTSUPP);
456		}
457		if (!powerof2(caps.ndis_nind)) {
458			if_printf(sc->hn_ifp, "RSS indirect table size is not "
459			    "power-of-2 %u\n", caps.ndis_nind);
460		}
461
462		if (bootverbose) {
463			if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
464			    caps.ndis_nind);
465		}
466		indsz = caps.ndis_nind;
467	} else {
468		indsz = NDIS_HASH_INDCNT;
469	}
470	if (indsz < rxr_cnt) {
471		if_printf(sc->hn_ifp, "# of RX rings (%d) > "
472		    "RSS indirect table size %d\n", rxr_cnt, indsz);
473		rxr_cnt = indsz;
474	}
475
476	/*
477	 * NOTE:
478	 * Toeplitz is at the lowest bit, and it is prefered; so ffs(),
479	 * instead of fls(), is used here.
480	 */
481	hash_fnidx = ffs(caps.ndis_caps & NDIS_RSS_CAP_HASHFUNC_MASK);
482	if (hash_fnidx == 0) {
483		if_printf(sc->hn_ifp, "no hash functions, caps 0x%08x\n",
484		    caps.ndis_caps);
485		return (EOPNOTSUPP);
486	}
487	hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */
488
489	if (caps.ndis_caps & NDIS_RSS_CAP_IPV4)
490		hash_types |= NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4;
491	if (caps.ndis_caps & NDIS_RSS_CAP_IPV6)
492		hash_types |= NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6;
493	if (caps.ndis_caps & NDIS_RSS_CAP_IPV6_EX)
494		hash_types |= NDIS_HASH_IPV6_EX | NDIS_HASH_TCP_IPV6_EX;
495	if (hash_types == 0) {
496		if_printf(sc->hn_ifp, "no hash types, caps 0x%08x\n",
497		    caps.ndis_caps);
498		return (EOPNOTSUPP);
499	}
500	if (bootverbose)
501		if_printf(sc->hn_ifp, "RSS caps %#x\n", caps.ndis_caps);
502
503	/* Commit! */
504	sc->hn_rss_ind_size = indsz;
505	sc->hn_rss_hash = hash_func | hash_types;
506	*rxr_cnt0 = rxr_cnt;
507	return (0);
508}
509
510static int
511hn_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;
555done:
556	vmbus_xact_put(xact);
557	return (error);
558}
559
560static int
561hn_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
729int
730hn_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
784int
785hn_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
803static int
804hn_rndis_init(struct hn_softc *sc)
805{
806	struct rndis_init_req *req;
807	const struct rndis_init_comp *comp;
808	struct vmbus_xact *xact;
809	size_t comp_len;
810	uint32_t rid;
811	int error;
812
813	xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
814	if (xact == NULL) {
815		if_printf(sc->hn_ifp, "no xact for RNDIS init\n");
816		return (ENXIO);
817	}
818	rid = hn_rndis_rid(sc);
819	req = vmbus_xact_req_data(xact);
820	req->rm_type = REMOTE_NDIS_INITIALIZE_MSG;
821	req->rm_len = sizeof(*req);
822	req->rm_rid = rid;
823	req->rm_ver_major = RNDIS_VERSION_MAJOR;
824	req->rm_ver_minor = RNDIS_VERSION_MINOR;
825	req->rm_max_xfersz = HN_RNDIS_XFER_SIZE;
826
827	comp_len = RNDIS_INIT_COMP_SIZE_MIN;
828	comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len,
829	    REMOTE_NDIS_INITIALIZE_CMPLT);
830	if (comp == NULL) {
831		if_printf(sc->hn_ifp, "exec RNDIS init failed\n");
832		error = EIO;
833		goto done;
834	}
835
836	if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
837		if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n",
838		    comp->rm_status);
839		error = EIO;
840		goto done;
841	}
842	sc->hn_rndis_agg_size = comp->rm_pktmaxsz;
843	sc->hn_rndis_agg_pkts = comp->rm_pktmaxcnt;
844	sc->hn_rndis_agg_align = 1U << comp->rm_align;
845
846	if (sc->hn_rndis_agg_align < sizeof(uint32_t)) {
847		/*
848		 * The RNDIS packet messsage encap assumes that the RNDIS
849		 * packet message is at least 4 bytes aligned.  Fix up the
850		 * alignment here, if the remote side sets the alignment
851		 * too low.
852		 */
853		if_printf(sc->hn_ifp, "fixup RNDIS aggpkt align: %u -> %zu\n",
854		    sc->hn_rndis_agg_align, sizeof(uint32_t));
855		sc->hn_rndis_agg_align = sizeof(uint32_t);
856	}
857
858	if (bootverbose) {
859		if_printf(sc->hn_ifp, "RNDIS ver %u.%u, "
860		    "aggpkt size %u, aggpkt cnt %u, aggpkt align %u\n",
861		    comp->rm_ver_major, comp->rm_ver_minor,
862		    sc->hn_rndis_agg_size, sc->hn_rndis_agg_pkts,
863		    sc->hn_rndis_agg_align);
864	}
865	error = 0;
866done:
867	vmbus_xact_put(xact);
868	return (error);
869}
870
871static int
872hn_rndis_halt(struct hn_softc *sc)
873{
874	struct vmbus_xact *xact;
875	struct rndis_halt_req *halt;
876	struct hn_nvs_sendctx sndc;
877	size_t comp_len;
878
879	xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt));
880	if (xact == NULL) {
881		if_printf(sc->hn_ifp, "no xact for RNDIS halt\n");
882		return (ENXIO);
883	}
884	halt = vmbus_xact_req_data(xact);
885	halt->rm_type = REMOTE_NDIS_HALT_MSG;
886	halt->rm_len = sizeof(*halt);
887	halt->rm_rid = hn_rndis_rid(sc);
888
889	/* No RNDIS completion; rely on NVS message send completion */
890	hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact);
891	hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len);
892
893	vmbus_xact_put(xact);
894	if (bootverbose)
895		if_printf(sc->hn_ifp, "RNDIS halt done\n");
896	return (0);
897}
898
899static int
900hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps)
901{
902	struct ndis_offload in;
903	size_t caps_len, size;
904	int error;
905
906	memset(&in, 0, sizeof(in));
907	in.ndis_hdr.ndis_type = NDIS_OBJTYPE_OFFLOAD;
908	if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) {
909		in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_3;
910		size = NDIS_OFFLOAD_SIZE;
911	} else if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_1) {
912		in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_2;
913		size = NDIS_OFFLOAD_SIZE_6_1;
914	} else {
915		in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1;
916		size = NDIS_OFFLOAD_SIZE_6_0;
917	}
918	in.ndis_hdr.ndis_size = size;
919
920	caps_len = NDIS_OFFLOAD_SIZE;
921	error = hn_rndis_query2(sc, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
922	    &in, size, caps, &caps_len, NDIS_OFFLOAD_SIZE_6_0);
923	if (error)
924		return (error);
925
926	/*
927	 * Preliminary verification.
928	 */
929	if (caps->ndis_hdr.ndis_type != NDIS_OBJTYPE_OFFLOAD) {
930		if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
931		    caps->ndis_hdr.ndis_type);
932		return (EINVAL);
933	}
934	if (caps->ndis_hdr.ndis_rev < NDIS_OFFLOAD_REV_1) {
935		if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
936		    caps->ndis_hdr.ndis_rev);
937		return (EINVAL);
938	}
939	if (caps->ndis_hdr.ndis_size > caps_len) {
940		if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
941		    "data size %zu\n", caps->ndis_hdr.ndis_size, caps_len);
942		return (EINVAL);
943	} else if (caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE_6_0) {
944		if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
945		    caps->ndis_hdr.ndis_size);
946		return (EINVAL);
947	}
948
949	if (bootverbose) {
950		/*
951		 * NOTE:
952		 * caps->ndis_hdr.ndis_size MUST be checked before accessing
953		 * NDIS 6.1+ specific fields.
954		 */
955		if_printf(sc->hn_ifp, "hwcaps rev %u\n",
956		    caps->ndis_hdr.ndis_rev);
957
958		if_printf(sc->hn_ifp, "hwcaps csum: "
959		    "ip4 tx 0x%x/0x%x rx 0x%x/0x%x, "
960		    "ip6 tx 0x%x/0x%x rx 0x%x/0x%x\n",
961		    caps->ndis_csum.ndis_ip4_txcsum,
962		    caps->ndis_csum.ndis_ip4_txenc,
963		    caps->ndis_csum.ndis_ip4_rxcsum,
964		    caps->ndis_csum.ndis_ip4_rxenc,
965		    caps->ndis_csum.ndis_ip6_txcsum,
966		    caps->ndis_csum.ndis_ip6_txenc,
967		    caps->ndis_csum.ndis_ip6_rxcsum,
968		    caps->ndis_csum.ndis_ip6_rxenc);
969		if_printf(sc->hn_ifp, "hwcaps lsov2: "
970		    "ip4 maxsz %u minsg %u encap 0x%x, "
971		    "ip6 maxsz %u minsg %u encap 0x%x opts 0x%x\n",
972		    caps->ndis_lsov2.ndis_ip4_maxsz,
973		    caps->ndis_lsov2.ndis_ip4_minsg,
974		    caps->ndis_lsov2.ndis_ip4_encap,
975		    caps->ndis_lsov2.ndis_ip6_maxsz,
976		    caps->ndis_lsov2.ndis_ip6_minsg,
977		    caps->ndis_lsov2.ndis_ip6_encap,
978		    caps->ndis_lsov2.ndis_ip6_opts);
979	}
980	return (0);
981}
982
983int
984hn_rndis_attach(struct hn_softc *sc, int mtu, int *init_done)
985{
986	int error;
987
988	*init_done = 0;
989
990	/*
991	 * Initialize RNDIS.
992	 */
993	error = hn_rndis_init(sc);
994	if (error)
995		return (error);
996	*init_done = 1;
997
998	/*
999	 * Configure NDIS offload settings.
1000	 */
1001	hn_rndis_conf_offload(sc, mtu);
1002	return (0);
1003}
1004
1005void
1006hn_rndis_detach(struct hn_softc *sc)
1007{
1008
1009	/* Halt the RNDIS. */
1010	hn_rndis_halt(sc);
1011}
1012