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