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