1250199Sgrehan/*-
2324461Ssephe * Copyright (c) 2009-2012,2016-2017 Microsoft Corp.
3250199Sgrehan * Copyright (c) 2010-2012 Citrix Inc.
4250199Sgrehan * Copyright (c) 2012 NetApp Inc.
5250199Sgrehan * All rights reserved.
6250199Sgrehan *
7250199Sgrehan * Redistribution and use in source and binary forms, with or without
8250199Sgrehan * modification, are permitted provided that the following conditions
9250199Sgrehan * are met:
10250199Sgrehan * 1. Redistributions of source code must retain the above copyright
11250199Sgrehan *    notice unmodified, this list of conditions, and the following
12250199Sgrehan *    disclaimer.
13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
14250199Sgrehan *    notice, this list of conditions and the following disclaimer in the
15250199Sgrehan *    documentation and/or other materials provided with the distribution.
16250199Sgrehan *
17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27250199Sgrehan */
28250199Sgrehan
29308504Ssephe/*
30308504Ssephe * Network Virtualization Service.
31250199Sgrehan */
32250199Sgrehan
33308504Ssephe#include <sys/cdefs.h>
34308504Ssephe__FBSDID("$FreeBSD: stable/10/sys/dev/hyperv/netvsc/hn_nvs.c 324574 2017-10-13 02:26:39Z sephe $");
35250199Sgrehan
36308504Ssephe#include "opt_inet6.h"
37308504Ssephe#include "opt_inet.h"
38308504Ssephe
39250199Sgrehan#include <sys/param.h>
40250199Sgrehan#include <sys/kernel.h>
41308504Ssephe#include <sys/limits.h>
42250199Sgrehan#include <sys/socket.h>
43308504Ssephe#include <sys/systm.h>
44308504Ssephe#include <sys/taskqueue.h>
45308504Ssephe
46324466Ssephe#include <net/ethernet.h>
47250199Sgrehan#include <net/if.h>
48250199Sgrehan#include <net/if_arp.h>
49308504Ssephe#include <net/if_media.h>
50250199Sgrehan
51308504Ssephe#include <netinet/in.h>
52308504Ssephe#include <netinet/tcp_lro.h>
53308504Ssephe
54250199Sgrehan#include <dev/hyperv/include/hyperv.h>
55308504Ssephe#include <dev/hyperv/include/hyperv_busdma.h>
56308504Ssephe#include <dev/hyperv/include/vmbus.h>
57307164Ssephe#include <dev/hyperv/include/vmbus_xact.h>
58308504Ssephe
59308504Ssephe#include <dev/hyperv/netvsc/ndis.h>
60307164Ssephe#include <dev/hyperv/netvsc/if_hnreg.h>
61307199Ssephe#include <dev/hyperv/netvsc/if_hnvar.h>
62308505Ssephe#include <dev/hyperv/netvsc/hn_nvs.h>
63250199Sgrehan
64308504Ssephestatic int			hn_nvs_conn_chim(struct hn_softc *);
65308504Ssephestatic int			hn_nvs_conn_rxbuf(struct hn_softc *);
66310757Ssephestatic void			hn_nvs_disconn_chim(struct hn_softc *);
67310757Ssephestatic void			hn_nvs_disconn_rxbuf(struct hn_softc *);
68308504Ssephestatic int			hn_nvs_conf_ndis(struct hn_softc *, int);
69308504Ssephestatic int			hn_nvs_init_ndis(struct hn_softc *);
70308504Ssephestatic int			hn_nvs_doinit(struct hn_softc *, uint32_t);
71308504Ssephestatic int			hn_nvs_init(struct hn_softc *);
72308504Ssephestatic const void		*hn_nvs_xact_execute(struct hn_softc *,
73308504Ssephe				    struct vmbus_xact *, void *, int,
74308504Ssephe				    size_t *, uint32_t);
75308504Ssephestatic void			hn_nvs_sent_none(struct hn_nvs_sendctx *,
76308504Ssephe				    struct hn_softc *, struct vmbus_channel *,
77308504Ssephe				    const void *, int);
78250199Sgrehan
79308504Ssephestruct hn_nvs_sendctx		hn_nvs_sendctx_none =
80308504Ssephe    HN_NVS_SENDCTX_INITIALIZER(hn_nvs_sent_none, NULL);
81307161Ssephe
82307200Ssephestatic const uint32_t		hn_nvs_version[] = {
83307200Ssephe	HN_NVS_VERSION_5,
84307200Ssephe	HN_NVS_VERSION_4,
85307200Ssephe	HN_NVS_VERSION_2,
86307200Ssephe	HN_NVS_VERSION_1
87307200Ssephe};
88307200Ssephe
89307206Ssephestatic const void *
90307174Ssephehn_nvs_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact,
91307186Ssephe    void *req, int reqlen, size_t *resplen0, uint32_t type)
92285236Swhu{
93308504Ssephe	struct hn_nvs_sendctx sndc;
94307186Ssephe	size_t resplen, min_resplen = *resplen0;
95307186Ssephe	const struct hn_nvs_hdr *hdr;
96307174Ssephe	int error;
97285236Swhu
98307186Ssephe	KASSERT(min_resplen >= sizeof(*hdr),
99307186Ssephe	    ("invalid minimum response len %zu", min_resplen));
100307186Ssephe
101307186Ssephe	/*
102307186Ssephe	 * Execute the xact setup by the caller.
103307186Ssephe	 */
104308504Ssephe	hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact);
105307186Ssephe
106307174Ssephe	vmbus_xact_activate(xact);
107307174Ssephe	error = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_RC,
108307174Ssephe	    req, reqlen, &sndc);
109307174Ssephe	if (error) {
110307174Ssephe		vmbus_xact_deactivate(xact);
111307186Ssephe		return (NULL);
112307174Ssephe	}
113310757Ssephe	hdr = vmbus_chan_xact_wait(sc->hn_prichan, xact, &resplen,
114310757Ssephe	    HN_CAN_SLEEP(sc));
115307186Ssephe
116307186Ssephe	/*
117307186Ssephe	 * Check this NVS response message.
118307186Ssephe	 */
119307186Ssephe	if (resplen < min_resplen) {
120307186Ssephe		if_printf(sc->hn_ifp, "invalid NVS resp len %zu\n", resplen);
121307186Ssephe		return (NULL);
122307186Ssephe	}
123307186Ssephe	if (hdr->nvs_type != type) {
124307186Ssephe		if_printf(sc->hn_ifp, "unexpected NVS resp 0x%08x, "
125307186Ssephe		    "expect 0x%08x\n", hdr->nvs_type, type);
126307186Ssephe		return (NULL);
127307186Ssephe	}
128307186Ssephe	/* All pass! */
129307186Ssephe	*resplen0 = resplen;
130307186Ssephe	return (hdr);
131307174Ssephe}
132285236Swhu
133307174Ssephestatic __inline int
134307174Ssephehn_nvs_req_send(struct hn_softc *sc, void *req, int reqlen)
135307174Ssephe{
136285236Swhu
137307174Ssephe	return (hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_NONE,
138308504Ssephe	    req, reqlen, &hn_nvs_sendctx_none));
139285236Swhu}
140285236Swhu
141250199Sgrehanstatic int
142307206Ssephehn_nvs_conn_rxbuf(struct hn_softc *sc)
143250199Sgrehan{
144307174Ssephe	struct vmbus_xact *xact = NULL;
145307164Ssephe	struct hn_nvs_rxbuf_conn *conn;
146307164Ssephe	const struct hn_nvs_rxbuf_connresp *resp;
147307164Ssephe	size_t resp_len;
148307164Ssephe	uint32_t status;
149307200Ssephe	int error, rxbuf_size;
150250199Sgrehan
151307200Ssephe	/*
152307200Ssephe	 * Limit RXBUF size for old NVS.
153307200Ssephe	 */
154307200Ssephe	if (sc->hn_nvs_ver <= HN_NVS_VERSION_2)
155308504Ssephe		rxbuf_size = HN_RXBUF_SIZE_COMPAT;
156307200Ssephe	else
157308504Ssephe		rxbuf_size = HN_RXBUF_SIZE;
158250199Sgrehan
159250199Sgrehan	/*
160307081Ssephe	 * Connect the RXBUF GPADL to the primary channel.
161307081Ssephe	 *
162307081Ssephe	 * NOTE:
163307081Ssephe	 * Only primary channel has RXBUF connected to it.  Sub-channels
164307081Ssephe	 * just share this RXBUF.
165250199Sgrehan	 */
166307164Ssephe	error = vmbus_chan_gpadl_connect(sc->hn_prichan,
167307174Ssephe	    sc->hn_rxbuf_dma.hv_paddr, rxbuf_size, &sc->hn_rxbuf_gpadl);
168307164Ssephe	if (error) {
169307206Ssephe		if_printf(sc->hn_ifp, "rxbuf gpadl conn failed: %d\n",
170307164Ssephe		    error);
171250199Sgrehan		goto cleanup;
172250199Sgrehan	}
173250199Sgrehan
174307164Ssephe	/*
175307164Ssephe	 * Connect RXBUF to NVS.
176307164Ssephe	 */
177250199Sgrehan
178307164Ssephe	xact = vmbus_xact_get(sc->hn_xact, sizeof(*conn));
179307164Ssephe	if (xact == NULL) {
180307164Ssephe		if_printf(sc->hn_ifp, "no xact for nvs rxbuf conn\n");
181307164Ssephe		error = ENXIO;
182307164Ssephe		goto cleanup;
183307164Ssephe	}
184307164Ssephe	conn = vmbus_xact_req_data(xact);
185307164Ssephe	conn->nvs_type = HN_NVS_TYPE_RXBUF_CONN;
186307174Ssephe	conn->nvs_gpadl = sc->hn_rxbuf_gpadl;
187307164Ssephe	conn->nvs_sig = HN_NVS_RXBUF_SIG;
188250199Sgrehan
189307186Ssephe	resp_len = sizeof(*resp);
190307186Ssephe	resp = hn_nvs_xact_execute(sc, xact, conn, sizeof(*conn), &resp_len,
191307186Ssephe	    HN_NVS_TYPE_RXBUF_CONNRESP);
192307174Ssephe	if (resp == NULL) {
193307206Ssephe		if_printf(sc->hn_ifp, "exec nvs rxbuf conn failed\n");
194307174Ssephe		error = EIO;
195250199Sgrehan		goto cleanup;
196250199Sgrehan	}
197250199Sgrehan
198307164Ssephe	status = resp->nvs_status;
199307164Ssephe	vmbus_xact_put(xact);
200307174Ssephe	xact = NULL;
201250199Sgrehan
202307164Ssephe	if (status != HN_NVS_STATUS_OK) {
203307206Ssephe		if_printf(sc->hn_ifp, "nvs rxbuf conn failed: %x\n", status);
204307164Ssephe		error = EIO;
205250199Sgrehan		goto cleanup;
206250199Sgrehan	}
207307174Ssephe	sc->hn_flags |= HN_FLAG_RXBUF_CONNECTED;
208250199Sgrehan
209307164Ssephe	return (0);
210250199Sgrehan
211250199Sgrehancleanup:
212307174Ssephe	if (xact != NULL)
213307174Ssephe		vmbus_xact_put(xact);
214307206Ssephe	hn_nvs_disconn_rxbuf(sc);
215307164Ssephe	return (error);
216250199Sgrehan}
217250199Sgrehan
218250199Sgrehanstatic int
219307206Ssephehn_nvs_conn_chim(struct hn_softc *sc)
220250199Sgrehan{
221307174Ssephe	struct vmbus_xact *xact = NULL;
222307164Ssephe	struct hn_nvs_chim_conn *chim;
223307164Ssephe	const struct hn_nvs_chim_connresp *resp;
224307164Ssephe	size_t resp_len;
225307164Ssephe	uint32_t status, sectsz;
226307164Ssephe	int error;
227250199Sgrehan
228250199Sgrehan	/*
229307081Ssephe	 * Connect chimney sending buffer GPADL to the primary channel.
230307081Ssephe	 *
231307081Ssephe	 * NOTE:
232307081Ssephe	 * Only primary channel has chimney sending buffer connected to it.
233307081Ssephe	 * Sub-channels just share this chimney sending buffer.
234250199Sgrehan	 */
235307164Ssephe	error = vmbus_chan_gpadl_connect(sc->hn_prichan,
236308504Ssephe  	    sc->hn_chim_dma.hv_paddr, HN_CHIM_SIZE, &sc->hn_chim_gpadl);
237307164Ssephe	if (error) {
238307206Ssephe		if_printf(sc->hn_ifp, "chim gpadl conn failed: %d\n", error);
239250199Sgrehan		goto cleanup;
240250199Sgrehan	}
241250199Sgrehan
242307164Ssephe	/*
243307164Ssephe	 * Connect chimney sending buffer to NVS
244307164Ssephe	 */
245250199Sgrehan
246307164Ssephe	xact = vmbus_xact_get(sc->hn_xact, sizeof(*chim));
247307164Ssephe	if (xact == NULL) {
248307164Ssephe		if_printf(sc->hn_ifp, "no xact for nvs chim conn\n");
249307164Ssephe		error = ENXIO;
250307164Ssephe		goto cleanup;
251307164Ssephe	}
252307164Ssephe	chim = vmbus_xact_req_data(xact);
253307164Ssephe	chim->nvs_type = HN_NVS_TYPE_CHIM_CONN;
254307174Ssephe	chim->nvs_gpadl = sc->hn_chim_gpadl;
255307164Ssephe	chim->nvs_sig = HN_NVS_CHIM_SIG;
256250199Sgrehan
257307186Ssephe	resp_len = sizeof(*resp);
258307186Ssephe	resp = hn_nvs_xact_execute(sc, xact, chim, sizeof(*chim), &resp_len,
259307186Ssephe	    HN_NVS_TYPE_CHIM_CONNRESP);
260307174Ssephe	if (resp == NULL) {
261307206Ssephe		if_printf(sc->hn_ifp, "exec nvs chim conn failed\n");
262307174Ssephe		error = EIO;
263250199Sgrehan		goto cleanup;
264250199Sgrehan	}
265250199Sgrehan
266307164Ssephe	status = resp->nvs_status;
267307164Ssephe	sectsz = resp->nvs_sectsz;
268307164Ssephe	vmbus_xact_put(xact);
269307174Ssephe	xact = NULL;
270307164Ssephe
271307164Ssephe	if (status != HN_NVS_STATUS_OK) {
272307206Ssephe		if_printf(sc->hn_ifp, "nvs chim conn failed: %x\n", status);
273307164Ssephe		error = EIO;
274250199Sgrehan		goto cleanup;
275250199Sgrehan	}
276315437Ssephe	if (sectsz == 0 || sectsz % sizeof(uint32_t) != 0) {
277310757Ssephe		/*
278310757Ssephe		 * Can't use chimney sending buffer; done!
279310757Ssephe		 */
280315437Ssephe		if (sectsz == 0) {
281315437Ssephe			if_printf(sc->hn_ifp, "zero chimney sending buffer "
282315437Ssephe			    "section size\n");
283315437Ssephe		} else {
284315437Ssephe			if_printf(sc->hn_ifp, "misaligned chimney sending "
285315437Ssephe			    "buffers, section size: %u\n", sectsz);
286315437Ssephe		}
287310757Ssephe		sc->hn_chim_szmax = 0;
288310757Ssephe		sc->hn_chim_cnt = 0;
289310757Ssephe		sc->hn_flags |= HN_FLAG_CHIM_CONNECTED;
290307206Ssephe		return (0);
291307164Ssephe	}
292250199Sgrehan
293307174Ssephe	sc->hn_chim_szmax = sectsz;
294308504Ssephe	sc->hn_chim_cnt = HN_CHIM_SIZE / sc->hn_chim_szmax;
295308504Ssephe	if (HN_CHIM_SIZE % sc->hn_chim_szmax != 0) {
296307174Ssephe		if_printf(sc->hn_ifp, "chimney sending sections are "
297307174Ssephe		    "not properly aligned\n");
298307174Ssephe	}
299307174Ssephe	if (sc->hn_chim_cnt % LONG_BIT != 0) {
300307174Ssephe		if_printf(sc->hn_ifp, "discard %d chimney sending sections\n",
301307174Ssephe		    sc->hn_chim_cnt % LONG_BIT);
302307174Ssephe	}
303250199Sgrehan
304307174Ssephe	sc->hn_chim_bmap_cnt = sc->hn_chim_cnt / LONG_BIT;
305307174Ssephe	sc->hn_chim_bmap = malloc(sc->hn_chim_bmap_cnt * sizeof(u_long),
306308503Ssephe	    M_DEVBUF, M_WAITOK | M_ZERO);
307307174Ssephe
308307174Ssephe	/* Done! */
309307174Ssephe	sc->hn_flags |= HN_FLAG_CHIM_CONNECTED;
310307164Ssephe	if (bootverbose) {
311307174Ssephe		if_printf(sc->hn_ifp, "chimney sending buffer %d/%d\n",
312307174Ssephe		    sc->hn_chim_szmax, sc->hn_chim_cnt);
313307164Ssephe	}
314307206Ssephe	return (0);
315250199Sgrehan
316250199Sgrehancleanup:
317307174Ssephe	if (xact != NULL)
318307174Ssephe		vmbus_xact_put(xact);
319307206Ssephe	hn_nvs_disconn_chim(sc);
320307164Ssephe	return (error);
321250199Sgrehan}
322250199Sgrehan
323310757Ssephestatic void
324307206Ssephehn_nvs_disconn_rxbuf(struct hn_softc *sc)
325250199Sgrehan{
326307206Ssephe	int error;
327250199Sgrehan
328307174Ssephe	if (sc->hn_flags & HN_FLAG_RXBUF_CONNECTED) {
329307164Ssephe		struct hn_nvs_rxbuf_disconn disconn;
330250199Sgrehan
331307164Ssephe		/*
332307164Ssephe		 * Disconnect RXBUF from NVS.
333307164Ssephe		 */
334307164Ssephe		memset(&disconn, 0, sizeof(disconn));
335307164Ssephe		disconn.nvs_type = HN_NVS_TYPE_RXBUF_DISCONN;
336307164Ssephe		disconn.nvs_sig = HN_NVS_RXBUF_SIG;
337250199Sgrehan
338307167Ssephe		/* NOTE: No response. */
339307206Ssephe		error = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
340307206Ssephe		if (error) {
341307174Ssephe			if_printf(sc->hn_ifp,
342307206Ssephe			    "send nvs rxbuf disconn failed: %d\n", error);
343310757Ssephe			/*
344310757Ssephe			 * Fine for a revoked channel, since the hypervisor
345310757Ssephe			 * does not drain TX bufring for a revoked channel.
346310757Ssephe			 */
347310757Ssephe			if (!vmbus_chan_is_revoked(sc->hn_prichan))
348310757Ssephe				sc->hn_flags |= HN_FLAG_RXBUF_REF;
349250199Sgrehan		}
350307174Ssephe		sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED;
351307250Ssephe
352307250Ssephe		/*
353307250Ssephe		 * Wait for the hypervisor to receive this NVS request.
354310743Ssephe		 *
355310743Ssephe		 * NOTE:
356310743Ssephe		 * The TX bufring will not be drained by the hypervisor,
357310743Ssephe		 * if the primary channel is revoked.
358307250Ssephe		 */
359310743Ssephe		while (!vmbus_chan_tx_empty(sc->hn_prichan) &&
360310743Ssephe		    !vmbus_chan_is_revoked(sc->hn_prichan))
361307250Ssephe			pause("waittx", 1);
362307250Ssephe		/*
363307250Ssephe		 * Linger long enough for NVS to disconnect RXBUF.
364307250Ssephe		 */
365307250Ssephe		pause("lingtx", (200 * hz) / 1000);
366250199Sgrehan	}
367307206Ssephe
368307174Ssephe	if (sc->hn_rxbuf_gpadl != 0) {
369250199Sgrehan		/*
370307174Ssephe		 * Disconnect RXBUF from primary channel.
371250199Sgrehan		 */
372307206Ssephe		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
373307174Ssephe		    sc->hn_rxbuf_gpadl);
374307206Ssephe		if (error) {
375307174Ssephe			if_printf(sc->hn_ifp,
376307206Ssephe			    "rxbuf gpadl disconn failed: %d\n", error);
377310757Ssephe			sc->hn_flags |= HN_FLAG_RXBUF_REF;
378250199Sgrehan		}
379307174Ssephe		sc->hn_rxbuf_gpadl = 0;
380250199Sgrehan	}
381250199Sgrehan}
382250199Sgrehan
383310757Ssephestatic void
384307206Ssephehn_nvs_disconn_chim(struct hn_softc *sc)
385250199Sgrehan{
386307206Ssephe	int error;
387250199Sgrehan
388307174Ssephe	if (sc->hn_flags & HN_FLAG_CHIM_CONNECTED) {
389307164Ssephe		struct hn_nvs_chim_disconn disconn;
390250199Sgrehan
391307164Ssephe		/*
392307164Ssephe		 * Disconnect chimney sending buffer from NVS.
393307164Ssephe		 */
394307164Ssephe		memset(&disconn, 0, sizeof(disconn));
395307164Ssephe		disconn.nvs_type = HN_NVS_TYPE_CHIM_DISCONN;
396307164Ssephe		disconn.nvs_sig = HN_NVS_CHIM_SIG;
397250199Sgrehan
398307167Ssephe		/* NOTE: No response. */
399307206Ssephe		error = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
400307206Ssephe		if (error) {
401307174Ssephe			if_printf(sc->hn_ifp,
402307206Ssephe			    "send nvs chim disconn failed: %d\n", error);
403310757Ssephe			/*
404310757Ssephe			 * Fine for a revoked channel, since the hypervisor
405310757Ssephe			 * does not drain TX bufring for a revoked channel.
406310757Ssephe			 */
407310757Ssephe			if (!vmbus_chan_is_revoked(sc->hn_prichan))
408310757Ssephe				sc->hn_flags |= HN_FLAG_CHIM_REF;
409250199Sgrehan		}
410307174Ssephe		sc->hn_flags &= ~HN_FLAG_CHIM_CONNECTED;
411307250Ssephe
412307250Ssephe		/*
413307250Ssephe		 * Wait for the hypervisor to receive this NVS request.
414310743Ssephe		 *
415310743Ssephe		 * NOTE:
416310743Ssephe		 * The TX bufring will not be drained by the hypervisor,
417310743Ssephe		 * if the primary channel is revoked.
418307250Ssephe		 */
419310743Ssephe		while (!vmbus_chan_tx_empty(sc->hn_prichan) &&
420310743Ssephe		    !vmbus_chan_is_revoked(sc->hn_prichan))
421307250Ssephe			pause("waittx", 1);
422307250Ssephe		/*
423307250Ssephe		 * Linger long enough for NVS to disconnect chimney
424307250Ssephe		 * sending buffer.
425307250Ssephe		 */
426307250Ssephe		pause("lingtx", (200 * hz) / 1000);
427250199Sgrehan	}
428307206Ssephe
429307174Ssephe	if (sc->hn_chim_gpadl != 0) {
430250199Sgrehan		/*
431307174Ssephe		 * Disconnect chimney sending buffer from primary channel.
432250199Sgrehan		 */
433307206Ssephe		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
434307174Ssephe		    sc->hn_chim_gpadl);
435307206Ssephe		if (error) {
436307174Ssephe			if_printf(sc->hn_ifp,
437307206Ssephe			    "chim gpadl disconn failed: %d\n", error);
438310757Ssephe			sc->hn_flags |= HN_FLAG_CHIM_REF;
439250199Sgrehan		}
440307174Ssephe		sc->hn_chim_gpadl = 0;
441250199Sgrehan	}
442250199Sgrehan
443307174Ssephe	if (sc->hn_chim_bmap != NULL) {
444308503Ssephe		free(sc->hn_chim_bmap, M_DEVBUF);
445307174Ssephe		sc->hn_chim_bmap = NULL;
446310757Ssephe		sc->hn_chim_bmap_cnt = 0;
447250199Sgrehan	}
448250199Sgrehan}
449250199Sgrehan
450250199Sgrehanstatic int
451307200Ssephehn_nvs_doinit(struct hn_softc *sc, uint32_t nvs_ver)
452250199Sgrehan{
453307164Ssephe	struct vmbus_xact *xact;
454307164Ssephe	struct hn_nvs_init *init;
455307164Ssephe	const struct hn_nvs_init_resp *resp;
456307164Ssephe	size_t resp_len;
457307164Ssephe	uint32_t status;
458250199Sgrehan
459307164Ssephe	xact = vmbus_xact_get(sc->hn_xact, sizeof(*init));
460307164Ssephe	if (xact == NULL) {
461307164Ssephe		if_printf(sc->hn_ifp, "no xact for nvs init\n");
462307164Ssephe		return (ENXIO);
463307164Ssephe	}
464307164Ssephe	init = vmbus_xact_req_data(xact);
465307164Ssephe	init->nvs_type = HN_NVS_TYPE_INIT;
466307164Ssephe	init->nvs_ver_min = nvs_ver;
467307164Ssephe	init->nvs_ver_max = nvs_ver;
468250199Sgrehan
469307186Ssephe	resp_len = sizeof(*resp);
470307186Ssephe	resp = hn_nvs_xact_execute(sc, xact, init, sizeof(*init), &resp_len,
471307186Ssephe	    HN_NVS_TYPE_INIT_RESP);
472307174Ssephe	if (resp == NULL) {
473307174Ssephe		if_printf(sc->hn_ifp, "exec init failed\n");
474307164Ssephe		vmbus_xact_put(xact);
475307174Ssephe		return (EIO);
476307164Ssephe	}
477250199Sgrehan
478307164Ssephe	status = resp->nvs_status;
479307164Ssephe	vmbus_xact_put(xact);
480307164Ssephe
481307164Ssephe	if (status != HN_NVS_STATUS_OK) {
482307246Ssephe		if (bootverbose) {
483307246Ssephe			/*
484307246Ssephe			 * Caller may try another NVS version, and will log
485307246Ssephe			 * error if there are no more NVS versions to try,
486307246Ssephe			 * so don't bark out loud here.
487307246Ssephe			 */
488307246Ssephe			if_printf(sc->hn_ifp, "nvs init failed for ver 0x%x\n",
489307246Ssephe			    nvs_ver);
490307246Ssephe		}
491250199Sgrehan		return (EINVAL);
492307164Ssephe	}
493250199Sgrehan	return (0);
494250199Sgrehan}
495250199Sgrehan
496250199Sgrehan/*
497307202Ssephe * Configure MTU and enable VLAN.
498250199Sgrehan */
499250199Sgrehanstatic int
500307202Ssephehn_nvs_conf_ndis(struct hn_softc *sc, int mtu)
501250199Sgrehan{
502307164Ssephe	struct hn_nvs_ndis_conf conf;
503307164Ssephe	int error;
504250199Sgrehan
505307164Ssephe	memset(&conf, 0, sizeof(conf));
506307164Ssephe	conf.nvs_type = HN_NVS_TYPE_NDIS_CONF;
507324466Ssephe	conf.nvs_mtu = mtu + ETHER_HDR_LEN;
508307164Ssephe	conf.nvs_caps = HN_NVS_NDIS_CONF_VLAN;
509314123Sdexuan	if (sc->hn_nvs_ver >= HN_NVS_VERSION_5)
510314123Sdexuan		conf.nvs_caps |= HN_NVS_NDIS_CONF_SRIOV;
511250199Sgrehan
512307167Ssephe	/* NOTE: No response. */
513307174Ssephe	error = hn_nvs_req_send(sc, &conf, sizeof(conf));
514307245Ssephe	if (error) {
515307164Ssephe		if_printf(sc->hn_ifp, "send nvs ndis conf failed: %d\n", error);
516307245Ssephe		return (error);
517307245Ssephe	}
518307245Ssephe
519307245Ssephe	if (bootverbose)
520307245Ssephe		if_printf(sc->hn_ifp, "nvs ndis conf done\n");
521307245Ssephe	sc->hn_caps |= HN_CAP_MTU | HN_CAP_VLAN;
522307245Ssephe	return (0);
523250199Sgrehan}
524250199Sgrehan
525250199Sgrehanstatic int
526307202Ssephehn_nvs_init_ndis(struct hn_softc *sc)
527307202Ssephe{
528307202Ssephe	struct hn_nvs_ndis_init ndis;
529307202Ssephe	int error;
530307202Ssephe
531307202Ssephe	memset(&ndis, 0, sizeof(ndis));
532307202Ssephe	ndis.nvs_type = HN_NVS_TYPE_NDIS_INIT;
533307202Ssephe	ndis.nvs_ndis_major = HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver);
534307202Ssephe	ndis.nvs_ndis_minor = HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver);
535307202Ssephe
536307202Ssephe	/* NOTE: No response. */
537307202Ssephe	error = hn_nvs_req_send(sc, &ndis, sizeof(ndis));
538307202Ssephe	if (error)
539307202Ssephe		if_printf(sc->hn_ifp, "send nvs ndis init failed: %d\n", error);
540307202Ssephe	return (error);
541307202Ssephe}
542307202Ssephe
543307202Ssephestatic int
544307200Ssephehn_nvs_init(struct hn_softc *sc)
545250199Sgrehan{
546307246Ssephe	int i, error;
547250199Sgrehan
548307245Ssephe	if (device_is_attached(sc->hn_dev)) {
549307245Ssephe		/*
550307245Ssephe		 * NVS version and NDIS version MUST NOT be changed.
551307245Ssephe		 */
552307245Ssephe		if (bootverbose) {
553307245Ssephe			if_printf(sc->hn_ifp, "reinit NVS version 0x%x, "
554307245Ssephe			    "NDIS version %u.%u\n", sc->hn_nvs_ver,
555307245Ssephe			    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
556307245Ssephe			    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
557307245Ssephe		}
558307246Ssephe
559307246Ssephe		error = hn_nvs_doinit(sc, sc->hn_nvs_ver);
560307246Ssephe		if (error) {
561307246Ssephe			if_printf(sc->hn_ifp, "reinit NVS version 0x%x "
562307246Ssephe			    "failed: %d\n", sc->hn_nvs_ver, error);
563308494Ssephe			return (error);
564307246Ssephe		}
565308494Ssephe		goto done;
566307245Ssephe	}
567307245Ssephe
568307245Ssephe	/*
569307245Ssephe	 * Find the supported NVS version and set NDIS version accordingly.
570307245Ssephe	 */
571307200Ssephe	for (i = 0; i < nitems(hn_nvs_version); ++i) {
572307200Ssephe		error = hn_nvs_doinit(sc, hn_nvs_version[i]);
573307200Ssephe		if (!error) {
574307200Ssephe			sc->hn_nvs_ver = hn_nvs_version[i];
575307200Ssephe
576307200Ssephe			/* Set NDIS version according to NVS version. */
577307199Ssephe			sc->hn_ndis_ver = HN_NDIS_VERSION_6_30;
578307200Ssephe			if (sc->hn_nvs_ver <= HN_NVS_VERSION_4)
579307199Ssephe				sc->hn_ndis_ver = HN_NDIS_VERSION_6_1;
580307200Ssephe
581307174Ssephe			if (bootverbose) {
582307181Ssephe				if_printf(sc->hn_ifp, "NVS version 0x%x, "
583307200Ssephe				    "NDIS version %u.%u\n", sc->hn_nvs_ver,
584307199Ssephe				    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
585307199Ssephe				    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
586307174Ssephe			}
587308494Ssephe			goto done;
588250199Sgrehan		}
589250199Sgrehan	}
590307200Ssephe	if_printf(sc->hn_ifp, "no NVS available\n");
591307200Ssephe	return (ENXIO);
592308494Ssephe
593308494Ssephedone:
594308494Ssephe	if (sc->hn_nvs_ver >= HN_NVS_VERSION_5)
595308494Ssephe		sc->hn_caps |= HN_CAP_HASHVAL;
596308494Ssephe	return (0);
597307200Ssephe}
598250199Sgrehan
599307206Ssepheint
600307206Ssephehn_nvs_attach(struct hn_softc *sc, int mtu)
601307200Ssephe{
602307206Ssephe	int error;
603285236Swhu
604324574Ssephe	if (hyperv_ver_major >= 10) {
605324574Ssephe		/* UDP 4-tuple hash is enforced. */
606324574Ssephe		sc->hn_caps |= HN_CAP_UDPHASH;
607324574Ssephe	}
608324574Ssephe
609307202Ssephe	/*
610307202Ssephe	 * Initialize NVS.
611307202Ssephe	 */
612307206Ssephe	error = hn_nvs_init(sc);
613307206Ssephe	if (error)
614307206Ssephe		return (error);
615307200Ssephe
616307202Ssephe	if (sc->hn_nvs_ver >= HN_NVS_VERSION_2) {
617307202Ssephe		/*
618307202Ssephe		 * Configure NDIS before initializing it.
619307202Ssephe		 */
620307206Ssephe		error = hn_nvs_conf_ndis(sc, mtu);
621307206Ssephe		if (error)
622307206Ssephe			return (error);
623307202Ssephe	}
624250199Sgrehan
625250199Sgrehan	/*
626307164Ssephe	 * Initialize NDIS.
627250199Sgrehan	 */
628307206Ssephe	error = hn_nvs_init_ndis(sc);
629307206Ssephe	if (error)
630307206Ssephe		return (error);
631250199Sgrehan
632307206Ssephe	/*
633307206Ssephe	 * Connect RXBUF.
634307206Ssephe	 */
635307206Ssephe	error = hn_nvs_conn_rxbuf(sc);
636307206Ssephe	if (error)
637307206Ssephe		return (error);
638307206Ssephe
639307206Ssephe	/*
640307206Ssephe	 * Connect chimney sending buffer.
641307206Ssephe	 */
642307206Ssephe	error = hn_nvs_conn_chim(sc);
643310757Ssephe	if (error) {
644310757Ssephe		hn_nvs_disconn_rxbuf(sc);
645307206Ssephe		return (error);
646310757Ssephe	}
647307206Ssephe	return (0);
648250199Sgrehan}
649250199Sgrehan
650307250Ssephevoid
651307250Ssephehn_nvs_detach(struct hn_softc *sc)
652250199Sgrehan{
653307250Ssephe
654307250Ssephe	/* NOTE: there are no requests to stop the NVS. */
655307206Ssephe	hn_nvs_disconn_rxbuf(sc);
656307206Ssephe	hn_nvs_disconn_chim(sc);
657250199Sgrehan}
658250199Sgrehan
659307185Ssephevoid
660308504Ssephehn_nvs_sent_xact(struct hn_nvs_sendctx *sndc,
661307174Ssephe    struct hn_softc *sc __unused, struct vmbus_channel *chan __unused,
662307167Ssephe    const void *data, int dlen)
663307161Ssephe{
664307164Ssephe
665307167Ssephe	vmbus_xact_wakeup(sndc->hn_cbarg, data, dlen);
666307161Ssephe}
667307161Ssephe
668307161Ssephestatic void
669308504Ssephehn_nvs_sent_none(struct hn_nvs_sendctx *sndc __unused,
670307174Ssephe    struct hn_softc *sc __unused, struct vmbus_channel *chan __unused,
671307167Ssephe    const void *data __unused, int dlen __unused)
672307161Ssephe{
673307161Ssephe	/* EMPTY */
674307161Ssephe}
675307161Ssephe
676250199Sgrehanint
677307206Ssephehn_nvs_alloc_subchans(struct hn_softc *sc, int *nsubch0)
678307206Ssephe{
679307206Ssephe	struct vmbus_xact *xact;
680307206Ssephe	struct hn_nvs_subch_req *req;
681307206Ssephe	const struct hn_nvs_subch_resp *resp;
682307206Ssephe	int error, nsubch_req;
683307206Ssephe	uint32_t nsubch;
684307206Ssephe	size_t resp_len;
685307206Ssephe
686307206Ssephe	nsubch_req = *nsubch0;
687307206Ssephe	KASSERT(nsubch_req > 0, ("invalid # of sub-channels %d", nsubch_req));
688307206Ssephe
689307206Ssephe	xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
690307206Ssephe	if (xact == NULL) {
691307206Ssephe		if_printf(sc->hn_ifp, "no xact for nvs subch alloc\n");
692307206Ssephe		return (ENXIO);
693307206Ssephe	}
694307206Ssephe	req = vmbus_xact_req_data(xact);
695307206Ssephe	req->nvs_type = HN_NVS_TYPE_SUBCH_REQ;
696307206Ssephe	req->nvs_op = HN_NVS_SUBCH_OP_ALLOC;
697307206Ssephe	req->nvs_nsubch = nsubch_req;
698307206Ssephe
699307206Ssephe	resp_len = sizeof(*resp);
700307206Ssephe	resp = hn_nvs_xact_execute(sc, xact, req, sizeof(*req), &resp_len,
701307206Ssephe	    HN_NVS_TYPE_SUBCH_RESP);
702307206Ssephe	if (resp == NULL) {
703307206Ssephe		if_printf(sc->hn_ifp, "exec nvs subch alloc failed\n");
704307206Ssephe		error = EIO;
705307206Ssephe		goto done;
706307206Ssephe	}
707307206Ssephe	if (resp->nvs_status != HN_NVS_STATUS_OK) {
708307206Ssephe		if_printf(sc->hn_ifp, "nvs subch alloc failed: %x\n",
709307206Ssephe		    resp->nvs_status);
710307206Ssephe		error = EIO;
711307206Ssephe		goto done;
712307206Ssephe	}
713307206Ssephe
714307206Ssephe	nsubch = resp->nvs_nsubch;
715307206Ssephe	if (nsubch > nsubch_req) {
716307206Ssephe		if_printf(sc->hn_ifp, "%u subchans are allocated, "
717307206Ssephe		    "requested %d\n", nsubch, nsubch_req);
718307206Ssephe		nsubch = nsubch_req;
719307206Ssephe	}
720307206Ssephe	*nsubch0 = nsubch;
721307206Ssephe	error = 0;
722307206Ssephedone:
723307206Ssephe	vmbus_xact_put(xact);
724307206Ssephe	return (error);
725307206Ssephe}
726308504Ssephe
727308504Ssepheint
728308504Ssephehn_nvs_send_rndis_ctrl(struct vmbus_channel *chan,
729308504Ssephe    struct hn_nvs_sendctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt)
730308504Ssephe{
731308504Ssephe
732308504Ssephe	return hn_nvs_send_rndis_sglist(chan, HN_NVS_RNDIS_MTYPE_CTRL,
733308504Ssephe	    sndc, gpa, gpa_cnt);
734308504Ssephe}
735314123Sdexuan
736314123Sdexuanvoid
737314123Sdexuanhn_nvs_set_datapath(struct hn_softc *sc, uint32_t path)
738314123Sdexuan{
739314123Sdexuan	struct hn_nvs_datapath dp;
740314123Sdexuan
741314123Sdexuan	memset(&dp, 0, sizeof(dp));
742314123Sdexuan	dp.nvs_type = HN_NVS_TYPE_SET_DATAPATH;
743314123Sdexuan	dp.nvs_active_path = path;
744314123Sdexuan
745314123Sdexuan	hn_nvs_req_send(sc, &dp, sizeof(dp));
746314123Sdexuan}
747