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