hn_nvs.c revision 314123
1250199Sgrehan/*-
2302045Ssephe * Copyright (c) 2009-2012,2016 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 314123 2017-02-23 07:04:17Z dexuan $");
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
46250199Sgrehan#include <net/if.h>
47250199Sgrehan#include <net/if_arp.h>
48308504Ssephe#include <net/if_media.h>
49250199Sgrehan
50308504Ssephe#include <netinet/in.h>
51308504Ssephe#include <netinet/tcp_lro.h>
52308504Ssephe
53250199Sgrehan#include <dev/hyperv/include/hyperv.h>
54308504Ssephe#include <dev/hyperv/include/hyperv_busdma.h>
55308504Ssephe#include <dev/hyperv/include/vmbus.h>
56307164Ssephe#include <dev/hyperv/include/vmbus_xact.h>
57308504Ssephe
58308504Ssephe#include <dev/hyperv/netvsc/ndis.h>
59307164Ssephe#include <dev/hyperv/netvsc/if_hnreg.h>
60307199Ssephe#include <dev/hyperv/netvsc/if_hnvar.h>
61308505Ssephe#include <dev/hyperv/netvsc/hn_nvs.h>
62250199Sgrehan
63308504Ssephestatic int			hn_nvs_conn_chim(struct hn_softc *);
64308504Ssephestatic int			hn_nvs_conn_rxbuf(struct hn_softc *);
65310757Ssephestatic void			hn_nvs_disconn_chim(struct hn_softc *);
66310757Ssephestatic void			hn_nvs_disconn_rxbuf(struct hn_softc *);
67308504Ssephestatic int			hn_nvs_conf_ndis(struct hn_softc *, int);
68308504Ssephestatic int			hn_nvs_init_ndis(struct hn_softc *);
69308504Ssephestatic int			hn_nvs_doinit(struct hn_softc *, uint32_t);
70308504Ssephestatic int			hn_nvs_init(struct hn_softc *);
71308504Ssephestatic const void		*hn_nvs_xact_execute(struct hn_softc *,
72308504Ssephe				    struct vmbus_xact *, void *, int,
73308504Ssephe				    size_t *, uint32_t);
74308504Ssephestatic void			hn_nvs_sent_none(struct hn_nvs_sendctx *,
75308504Ssephe				    struct hn_softc *, struct vmbus_channel *,
76308504Ssephe				    const void *, int);
77250199Sgrehan
78308504Ssephestruct hn_nvs_sendctx		hn_nvs_sendctx_none =
79308504Ssephe    HN_NVS_SENDCTX_INITIALIZER(hn_nvs_sent_none, NULL);
80307161Ssephe
81307200Ssephestatic const uint32_t		hn_nvs_version[] = {
82307200Ssephe	HN_NVS_VERSION_5,
83307200Ssephe	HN_NVS_VERSION_4,
84307200Ssephe	HN_NVS_VERSION_2,
85307200Ssephe	HN_NVS_VERSION_1
86307200Ssephe};
87307200Ssephe
88307206Ssephestatic const void *
89307174Ssephehn_nvs_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact,
90307186Ssephe    void *req, int reqlen, size_t *resplen0, uint32_t type)
91285236Swhu{
92308504Ssephe	struct hn_nvs_sendctx sndc;
93307186Ssephe	size_t resplen, min_resplen = *resplen0;
94307186Ssephe	const struct hn_nvs_hdr *hdr;
95307174Ssephe	int error;
96285236Swhu
97307186Ssephe	KASSERT(min_resplen >= sizeof(*hdr),
98307186Ssephe	    ("invalid minimum response len %zu", min_resplen));
99307186Ssephe
100307186Ssephe	/*
101307186Ssephe	 * Execute the xact setup by the caller.
102307186Ssephe	 */
103308504Ssephe	hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact);
104307186Ssephe
105307174Ssephe	vmbus_xact_activate(xact);
106307174Ssephe	error = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_RC,
107307174Ssephe	    req, reqlen, &sndc);
108307174Ssephe	if (error) {
109307174Ssephe		vmbus_xact_deactivate(xact);
110307186Ssephe		return (NULL);
111307174Ssephe	}
112310757Ssephe	hdr = vmbus_chan_xact_wait(sc->hn_prichan, xact, &resplen,
113310757Ssephe	    HN_CAN_SLEEP(sc));
114307186Ssephe
115307186Ssephe	/*
116307186Ssephe	 * Check this NVS response message.
117307186Ssephe	 */
118307186Ssephe	if (resplen < min_resplen) {
119307186Ssephe		if_printf(sc->hn_ifp, "invalid NVS resp len %zu\n", resplen);
120307186Ssephe		return (NULL);
121307186Ssephe	}
122307186Ssephe	if (hdr->nvs_type != type) {
123307186Ssephe		if_printf(sc->hn_ifp, "unexpected NVS resp 0x%08x, "
124307186Ssephe		    "expect 0x%08x\n", hdr->nvs_type, type);
125307186Ssephe		return (NULL);
126307186Ssephe	}
127307186Ssephe	/* All pass! */
128307186Ssephe	*resplen0 = resplen;
129307186Ssephe	return (hdr);
130307174Ssephe}
131285236Swhu
132307174Ssephestatic __inline int
133307174Ssephehn_nvs_req_send(struct hn_softc *sc, void *req, int reqlen)
134307174Ssephe{
135285236Swhu
136307174Ssephe	return (hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_NONE,
137308504Ssephe	    req, reqlen, &hn_nvs_sendctx_none));
138285236Swhu}
139285236Swhu
140250199Sgrehanstatic int
141307206Ssephehn_nvs_conn_rxbuf(struct hn_softc *sc)
142250199Sgrehan{
143307174Ssephe	struct vmbus_xact *xact = NULL;
144307164Ssephe	struct hn_nvs_rxbuf_conn *conn;
145307164Ssephe	const struct hn_nvs_rxbuf_connresp *resp;
146307164Ssephe	size_t resp_len;
147307164Ssephe	uint32_t status;
148307200Ssephe	int error, rxbuf_size;
149250199Sgrehan
150307200Ssephe	/*
151307200Ssephe	 * Limit RXBUF size for old NVS.
152307200Ssephe	 */
153307200Ssephe	if (sc->hn_nvs_ver <= HN_NVS_VERSION_2)
154308504Ssephe		rxbuf_size = HN_RXBUF_SIZE_COMPAT;
155307200Ssephe	else
156308504Ssephe		rxbuf_size = HN_RXBUF_SIZE;
157250199Sgrehan
158250199Sgrehan	/*
159307081Ssephe	 * Connect the RXBUF GPADL to the primary channel.
160307081Ssephe	 *
161307081Ssephe	 * NOTE:
162307081Ssephe	 * Only primary channel has RXBUF connected to it.  Sub-channels
163307081Ssephe	 * just share this RXBUF.
164250199Sgrehan	 */
165307164Ssephe	error = vmbus_chan_gpadl_connect(sc->hn_prichan,
166307174Ssephe	    sc->hn_rxbuf_dma.hv_paddr, rxbuf_size, &sc->hn_rxbuf_gpadl);
167307164Ssephe	if (error) {
168307206Ssephe		if_printf(sc->hn_ifp, "rxbuf gpadl conn failed: %d\n",
169307164Ssephe		    error);
170250199Sgrehan		goto cleanup;
171250199Sgrehan	}
172250199Sgrehan
173307164Ssephe	/*
174307164Ssephe	 * Connect RXBUF to NVS.
175307164Ssephe	 */
176250199Sgrehan
177307164Ssephe	xact = vmbus_xact_get(sc->hn_xact, sizeof(*conn));
178307164Ssephe	if (xact == NULL) {
179307164Ssephe		if_printf(sc->hn_ifp, "no xact for nvs rxbuf conn\n");
180307164Ssephe		error = ENXIO;
181307164Ssephe		goto cleanup;
182307164Ssephe	}
183307164Ssephe	conn = vmbus_xact_req_data(xact);
184307164Ssephe	conn->nvs_type = HN_NVS_TYPE_RXBUF_CONN;
185307174Ssephe	conn->nvs_gpadl = sc->hn_rxbuf_gpadl;
186307164Ssephe	conn->nvs_sig = HN_NVS_RXBUF_SIG;
187250199Sgrehan
188307186Ssephe	resp_len = sizeof(*resp);
189307186Ssephe	resp = hn_nvs_xact_execute(sc, xact, conn, sizeof(*conn), &resp_len,
190307186Ssephe	    HN_NVS_TYPE_RXBUF_CONNRESP);
191307174Ssephe	if (resp == NULL) {
192307206Ssephe		if_printf(sc->hn_ifp, "exec nvs rxbuf conn failed\n");
193307174Ssephe		error = EIO;
194250199Sgrehan		goto cleanup;
195250199Sgrehan	}
196250199Sgrehan
197307164Ssephe	status = resp->nvs_status;
198307164Ssephe	vmbus_xact_put(xact);
199307174Ssephe	xact = NULL;
200250199Sgrehan
201307164Ssephe	if (status != HN_NVS_STATUS_OK) {
202307206Ssephe		if_printf(sc->hn_ifp, "nvs rxbuf conn failed: %x\n", status);
203307164Ssephe		error = EIO;
204250199Sgrehan		goto cleanup;
205250199Sgrehan	}
206307174Ssephe	sc->hn_flags |= HN_FLAG_RXBUF_CONNECTED;
207250199Sgrehan
208307164Ssephe	return (0);
209250199Sgrehan
210250199Sgrehancleanup:
211307174Ssephe	if (xact != NULL)
212307174Ssephe		vmbus_xact_put(xact);
213307206Ssephe	hn_nvs_disconn_rxbuf(sc);
214307164Ssephe	return (error);
215250199Sgrehan}
216250199Sgrehan
217250199Sgrehanstatic int
218307206Ssephehn_nvs_conn_chim(struct hn_softc *sc)
219250199Sgrehan{
220307174Ssephe	struct vmbus_xact *xact = NULL;
221307164Ssephe	struct hn_nvs_chim_conn *chim;
222307164Ssephe	const struct hn_nvs_chim_connresp *resp;
223307164Ssephe	size_t resp_len;
224307164Ssephe	uint32_t status, sectsz;
225307164Ssephe	int error;
226250199Sgrehan
227250199Sgrehan	/*
228307081Ssephe	 * Connect chimney sending buffer GPADL to the primary channel.
229307081Ssephe	 *
230307081Ssephe	 * NOTE:
231307081Ssephe	 * Only primary channel has chimney sending buffer connected to it.
232307081Ssephe	 * Sub-channels just share this chimney sending buffer.
233250199Sgrehan	 */
234307164Ssephe	error = vmbus_chan_gpadl_connect(sc->hn_prichan,
235308504Ssephe  	    sc->hn_chim_dma.hv_paddr, HN_CHIM_SIZE, &sc->hn_chim_gpadl);
236307164Ssephe	if (error) {
237307206Ssephe		if_printf(sc->hn_ifp, "chim gpadl conn failed: %d\n", error);
238250199Sgrehan		goto cleanup;
239250199Sgrehan	}
240250199Sgrehan
241307164Ssephe	/*
242307164Ssephe	 * Connect chimney sending buffer to NVS
243307164Ssephe	 */
244250199Sgrehan
245307164Ssephe	xact = vmbus_xact_get(sc->hn_xact, sizeof(*chim));
246307164Ssephe	if (xact == NULL) {
247307164Ssephe		if_printf(sc->hn_ifp, "no xact for nvs chim conn\n");
248307164Ssephe		error = ENXIO;
249307164Ssephe		goto cleanup;
250307164Ssephe	}
251307164Ssephe	chim = vmbus_xact_req_data(xact);
252307164Ssephe	chim->nvs_type = HN_NVS_TYPE_CHIM_CONN;
253307174Ssephe	chim->nvs_gpadl = sc->hn_chim_gpadl;
254307164Ssephe	chim->nvs_sig = HN_NVS_CHIM_SIG;
255250199Sgrehan
256307186Ssephe	resp_len = sizeof(*resp);
257307186Ssephe	resp = hn_nvs_xact_execute(sc, xact, chim, sizeof(*chim), &resp_len,
258307186Ssephe	    HN_NVS_TYPE_CHIM_CONNRESP);
259307174Ssephe	if (resp == NULL) {
260307206Ssephe		if_printf(sc->hn_ifp, "exec nvs chim conn failed\n");
261307174Ssephe		error = EIO;
262250199Sgrehan		goto cleanup;
263250199Sgrehan	}
264250199Sgrehan
265307164Ssephe	status = resp->nvs_status;
266307164Ssephe	sectsz = resp->nvs_sectsz;
267307164Ssephe	vmbus_xact_put(xact);
268307174Ssephe	xact = NULL;
269307164Ssephe
270307164Ssephe	if (status != HN_NVS_STATUS_OK) {
271307206Ssephe		if_printf(sc->hn_ifp, "nvs chim conn failed: %x\n", status);
272307164Ssephe		error = EIO;
273250199Sgrehan		goto cleanup;
274250199Sgrehan	}
275307164Ssephe	if (sectsz == 0) {
276310757Ssephe		/*
277310757Ssephe		 * Can't use chimney sending buffer; done!
278310757Ssephe		 */
279307164Ssephe		if_printf(sc->hn_ifp, "zero chimney sending buffer "
280307164Ssephe		    "section size\n");
281310757Ssephe		sc->hn_chim_szmax = 0;
282310757Ssephe		sc->hn_chim_cnt = 0;
283310757Ssephe		sc->hn_flags |= HN_FLAG_CHIM_CONNECTED;
284307206Ssephe		return (0);
285307164Ssephe	}
286250199Sgrehan
287307174Ssephe	sc->hn_chim_szmax = sectsz;
288308504Ssephe	sc->hn_chim_cnt = HN_CHIM_SIZE / sc->hn_chim_szmax;
289308504Ssephe	if (HN_CHIM_SIZE % sc->hn_chim_szmax != 0) {
290307174Ssephe		if_printf(sc->hn_ifp, "chimney sending sections are "
291307174Ssephe		    "not properly aligned\n");
292307174Ssephe	}
293307174Ssephe	if (sc->hn_chim_cnt % LONG_BIT != 0) {
294307174Ssephe		if_printf(sc->hn_ifp, "discard %d chimney sending sections\n",
295307174Ssephe		    sc->hn_chim_cnt % LONG_BIT);
296307174Ssephe	}
297250199Sgrehan
298307174Ssephe	sc->hn_chim_bmap_cnt = sc->hn_chim_cnt / LONG_BIT;
299307174Ssephe	sc->hn_chim_bmap = malloc(sc->hn_chim_bmap_cnt * sizeof(u_long),
300308503Ssephe	    M_DEVBUF, M_WAITOK | M_ZERO);
301307174Ssephe
302307174Ssephe	/* Done! */
303307174Ssephe	sc->hn_flags |= HN_FLAG_CHIM_CONNECTED;
304307164Ssephe	if (bootverbose) {
305307174Ssephe		if_printf(sc->hn_ifp, "chimney sending buffer %d/%d\n",
306307174Ssephe		    sc->hn_chim_szmax, sc->hn_chim_cnt);
307307164Ssephe	}
308307206Ssephe	return (0);
309250199Sgrehan
310250199Sgrehancleanup:
311307174Ssephe	if (xact != NULL)
312307174Ssephe		vmbus_xact_put(xact);
313307206Ssephe	hn_nvs_disconn_chim(sc);
314307164Ssephe	return (error);
315250199Sgrehan}
316250199Sgrehan
317310757Ssephestatic void
318307206Ssephehn_nvs_disconn_rxbuf(struct hn_softc *sc)
319250199Sgrehan{
320307206Ssephe	int error;
321250199Sgrehan
322307174Ssephe	if (sc->hn_flags & HN_FLAG_RXBUF_CONNECTED) {
323307164Ssephe		struct hn_nvs_rxbuf_disconn disconn;
324250199Sgrehan
325307164Ssephe		/*
326307164Ssephe		 * Disconnect RXBUF from NVS.
327307164Ssephe		 */
328307164Ssephe		memset(&disconn, 0, sizeof(disconn));
329307164Ssephe		disconn.nvs_type = HN_NVS_TYPE_RXBUF_DISCONN;
330307164Ssephe		disconn.nvs_sig = HN_NVS_RXBUF_SIG;
331250199Sgrehan
332307167Ssephe		/* NOTE: No response. */
333307206Ssephe		error = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
334307206Ssephe		if (error) {
335307174Ssephe			if_printf(sc->hn_ifp,
336307206Ssephe			    "send nvs rxbuf disconn failed: %d\n", error);
337310757Ssephe			/*
338310757Ssephe			 * Fine for a revoked channel, since the hypervisor
339310757Ssephe			 * does not drain TX bufring for a revoked channel.
340310757Ssephe			 */
341310757Ssephe			if (!vmbus_chan_is_revoked(sc->hn_prichan))
342310757Ssephe				sc->hn_flags |= HN_FLAG_RXBUF_REF;
343250199Sgrehan		}
344307174Ssephe		sc->hn_flags &= ~HN_FLAG_RXBUF_CONNECTED;
345307250Ssephe
346307250Ssephe		/*
347307250Ssephe		 * Wait for the hypervisor to receive this NVS request.
348310743Ssephe		 *
349310743Ssephe		 * NOTE:
350310743Ssephe		 * The TX bufring will not be drained by the hypervisor,
351310743Ssephe		 * if the primary channel is revoked.
352307250Ssephe		 */
353310743Ssephe		while (!vmbus_chan_tx_empty(sc->hn_prichan) &&
354310743Ssephe		    !vmbus_chan_is_revoked(sc->hn_prichan))
355307250Ssephe			pause("waittx", 1);
356307250Ssephe		/*
357307250Ssephe		 * Linger long enough for NVS to disconnect RXBUF.
358307250Ssephe		 */
359307250Ssephe		pause("lingtx", (200 * hz) / 1000);
360250199Sgrehan	}
361307206Ssephe
362307174Ssephe	if (sc->hn_rxbuf_gpadl != 0) {
363250199Sgrehan		/*
364307174Ssephe		 * Disconnect RXBUF from primary channel.
365250199Sgrehan		 */
366307206Ssephe		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
367307174Ssephe		    sc->hn_rxbuf_gpadl);
368307206Ssephe		if (error) {
369307174Ssephe			if_printf(sc->hn_ifp,
370307206Ssephe			    "rxbuf gpadl disconn failed: %d\n", error);
371310757Ssephe			sc->hn_flags |= HN_FLAG_RXBUF_REF;
372250199Sgrehan		}
373307174Ssephe		sc->hn_rxbuf_gpadl = 0;
374250199Sgrehan	}
375250199Sgrehan}
376250199Sgrehan
377310757Ssephestatic void
378307206Ssephehn_nvs_disconn_chim(struct hn_softc *sc)
379250199Sgrehan{
380307206Ssephe	int error;
381250199Sgrehan
382307174Ssephe	if (sc->hn_flags & HN_FLAG_CHIM_CONNECTED) {
383307164Ssephe		struct hn_nvs_chim_disconn disconn;
384250199Sgrehan
385307164Ssephe		/*
386307164Ssephe		 * Disconnect chimney sending buffer from NVS.
387307164Ssephe		 */
388307164Ssephe		memset(&disconn, 0, sizeof(disconn));
389307164Ssephe		disconn.nvs_type = HN_NVS_TYPE_CHIM_DISCONN;
390307164Ssephe		disconn.nvs_sig = HN_NVS_CHIM_SIG;
391250199Sgrehan
392307167Ssephe		/* NOTE: No response. */
393307206Ssephe		error = hn_nvs_req_send(sc, &disconn, sizeof(disconn));
394307206Ssephe		if (error) {
395307174Ssephe			if_printf(sc->hn_ifp,
396307206Ssephe			    "send nvs chim disconn failed: %d\n", error);
397310757Ssephe			/*
398310757Ssephe			 * Fine for a revoked channel, since the hypervisor
399310757Ssephe			 * does not drain TX bufring for a revoked channel.
400310757Ssephe			 */
401310757Ssephe			if (!vmbus_chan_is_revoked(sc->hn_prichan))
402310757Ssephe				sc->hn_flags |= HN_FLAG_CHIM_REF;
403250199Sgrehan		}
404307174Ssephe		sc->hn_flags &= ~HN_FLAG_CHIM_CONNECTED;
405307250Ssephe
406307250Ssephe		/*
407307250Ssephe		 * Wait for the hypervisor to receive this NVS request.
408310743Ssephe		 *
409310743Ssephe		 * NOTE:
410310743Ssephe		 * The TX bufring will not be drained by the hypervisor,
411310743Ssephe		 * if the primary channel is revoked.
412307250Ssephe		 */
413310743Ssephe		while (!vmbus_chan_tx_empty(sc->hn_prichan) &&
414310743Ssephe		    !vmbus_chan_is_revoked(sc->hn_prichan))
415307250Ssephe			pause("waittx", 1);
416307250Ssephe		/*
417307250Ssephe		 * Linger long enough for NVS to disconnect chimney
418307250Ssephe		 * sending buffer.
419307250Ssephe		 */
420307250Ssephe		pause("lingtx", (200 * hz) / 1000);
421250199Sgrehan	}
422307206Ssephe
423307174Ssephe	if (sc->hn_chim_gpadl != 0) {
424250199Sgrehan		/*
425307174Ssephe		 * Disconnect chimney sending buffer from primary channel.
426250199Sgrehan		 */
427307206Ssephe		error = vmbus_chan_gpadl_disconnect(sc->hn_prichan,
428307174Ssephe		    sc->hn_chim_gpadl);
429307206Ssephe		if (error) {
430307174Ssephe			if_printf(sc->hn_ifp,
431307206Ssephe			    "chim gpadl disconn failed: %d\n", error);
432310757Ssephe			sc->hn_flags |= HN_FLAG_CHIM_REF;
433250199Sgrehan		}
434307174Ssephe		sc->hn_chim_gpadl = 0;
435250199Sgrehan	}
436250199Sgrehan
437307174Ssephe	if (sc->hn_chim_bmap != NULL) {
438308503Ssephe		free(sc->hn_chim_bmap, M_DEVBUF);
439307174Ssephe		sc->hn_chim_bmap = NULL;
440310757Ssephe		sc->hn_chim_bmap_cnt = 0;
441250199Sgrehan	}
442250199Sgrehan}
443250199Sgrehan
444250199Sgrehanstatic int
445307200Ssephehn_nvs_doinit(struct hn_softc *sc, uint32_t nvs_ver)
446250199Sgrehan{
447307164Ssephe	struct vmbus_xact *xact;
448307164Ssephe	struct hn_nvs_init *init;
449307164Ssephe	const struct hn_nvs_init_resp *resp;
450307164Ssephe	size_t resp_len;
451307164Ssephe	uint32_t status;
452250199Sgrehan
453307164Ssephe	xact = vmbus_xact_get(sc->hn_xact, sizeof(*init));
454307164Ssephe	if (xact == NULL) {
455307164Ssephe		if_printf(sc->hn_ifp, "no xact for nvs init\n");
456307164Ssephe		return (ENXIO);
457307164Ssephe	}
458307164Ssephe	init = vmbus_xact_req_data(xact);
459307164Ssephe	init->nvs_type = HN_NVS_TYPE_INIT;
460307164Ssephe	init->nvs_ver_min = nvs_ver;
461307164Ssephe	init->nvs_ver_max = nvs_ver;
462250199Sgrehan
463307186Ssephe	resp_len = sizeof(*resp);
464307186Ssephe	resp = hn_nvs_xact_execute(sc, xact, init, sizeof(*init), &resp_len,
465307186Ssephe	    HN_NVS_TYPE_INIT_RESP);
466307174Ssephe	if (resp == NULL) {
467307174Ssephe		if_printf(sc->hn_ifp, "exec init failed\n");
468307164Ssephe		vmbus_xact_put(xact);
469307174Ssephe		return (EIO);
470307164Ssephe	}
471250199Sgrehan
472307164Ssephe	status = resp->nvs_status;
473307164Ssephe	vmbus_xact_put(xact);
474307164Ssephe
475307164Ssephe	if (status != HN_NVS_STATUS_OK) {
476307246Ssephe		if (bootverbose) {
477307246Ssephe			/*
478307246Ssephe			 * Caller may try another NVS version, and will log
479307246Ssephe			 * error if there are no more NVS versions to try,
480307246Ssephe			 * so don't bark out loud here.
481307246Ssephe			 */
482307246Ssephe			if_printf(sc->hn_ifp, "nvs init failed for ver 0x%x\n",
483307246Ssephe			    nvs_ver);
484307246Ssephe		}
485250199Sgrehan		return (EINVAL);
486307164Ssephe	}
487250199Sgrehan	return (0);
488250199Sgrehan}
489250199Sgrehan
490250199Sgrehan/*
491307202Ssephe * Configure MTU and enable VLAN.
492250199Sgrehan */
493250199Sgrehanstatic int
494307202Ssephehn_nvs_conf_ndis(struct hn_softc *sc, int mtu)
495250199Sgrehan{
496307164Ssephe	struct hn_nvs_ndis_conf conf;
497307164Ssephe	int error;
498250199Sgrehan
499307164Ssephe	memset(&conf, 0, sizeof(conf));
500307164Ssephe	conf.nvs_type = HN_NVS_TYPE_NDIS_CONF;
501307164Ssephe	conf.nvs_mtu = mtu;
502307164Ssephe	conf.nvs_caps = HN_NVS_NDIS_CONF_VLAN;
503314123Sdexuan	if (sc->hn_nvs_ver >= HN_NVS_VERSION_5)
504314123Sdexuan		conf.nvs_caps |= HN_NVS_NDIS_CONF_SRIOV;
505250199Sgrehan
506307167Ssephe	/* NOTE: No response. */
507307174Ssephe	error = hn_nvs_req_send(sc, &conf, sizeof(conf));
508307245Ssephe	if (error) {
509307164Ssephe		if_printf(sc->hn_ifp, "send nvs ndis conf failed: %d\n", error);
510307245Ssephe		return (error);
511307245Ssephe	}
512307245Ssephe
513307245Ssephe	if (bootverbose)
514307245Ssephe		if_printf(sc->hn_ifp, "nvs ndis conf done\n");
515307245Ssephe	sc->hn_caps |= HN_CAP_MTU | HN_CAP_VLAN;
516307245Ssephe	return (0);
517250199Sgrehan}
518250199Sgrehan
519250199Sgrehanstatic int
520307202Ssephehn_nvs_init_ndis(struct hn_softc *sc)
521307202Ssephe{
522307202Ssephe	struct hn_nvs_ndis_init ndis;
523307202Ssephe	int error;
524307202Ssephe
525307202Ssephe	memset(&ndis, 0, sizeof(ndis));
526307202Ssephe	ndis.nvs_type = HN_NVS_TYPE_NDIS_INIT;
527307202Ssephe	ndis.nvs_ndis_major = HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver);
528307202Ssephe	ndis.nvs_ndis_minor = HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver);
529307202Ssephe
530307202Ssephe	/* NOTE: No response. */
531307202Ssephe	error = hn_nvs_req_send(sc, &ndis, sizeof(ndis));
532307202Ssephe	if (error)
533307202Ssephe		if_printf(sc->hn_ifp, "send nvs ndis init failed: %d\n", error);
534307202Ssephe	return (error);
535307202Ssephe}
536307202Ssephe
537307202Ssephestatic int
538307200Ssephehn_nvs_init(struct hn_softc *sc)
539250199Sgrehan{
540307246Ssephe	int i, error;
541250199Sgrehan
542307245Ssephe	if (device_is_attached(sc->hn_dev)) {
543307245Ssephe		/*
544307245Ssephe		 * NVS version and NDIS version MUST NOT be changed.
545307245Ssephe		 */
546307245Ssephe		if (bootverbose) {
547307245Ssephe			if_printf(sc->hn_ifp, "reinit NVS version 0x%x, "
548307245Ssephe			    "NDIS version %u.%u\n", sc->hn_nvs_ver,
549307245Ssephe			    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
550307245Ssephe			    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
551307245Ssephe		}
552307246Ssephe
553307246Ssephe		error = hn_nvs_doinit(sc, sc->hn_nvs_ver);
554307246Ssephe		if (error) {
555307246Ssephe			if_printf(sc->hn_ifp, "reinit NVS version 0x%x "
556307246Ssephe			    "failed: %d\n", sc->hn_nvs_ver, error);
557308494Ssephe			return (error);
558307246Ssephe		}
559308494Ssephe		goto done;
560307245Ssephe	}
561307245Ssephe
562307245Ssephe	/*
563307245Ssephe	 * Find the supported NVS version and set NDIS version accordingly.
564307245Ssephe	 */
565307200Ssephe	for (i = 0; i < nitems(hn_nvs_version); ++i) {
566307200Ssephe		error = hn_nvs_doinit(sc, hn_nvs_version[i]);
567307200Ssephe		if (!error) {
568307200Ssephe			sc->hn_nvs_ver = hn_nvs_version[i];
569307200Ssephe
570307200Ssephe			/* Set NDIS version according to NVS version. */
571307199Ssephe			sc->hn_ndis_ver = HN_NDIS_VERSION_6_30;
572307200Ssephe			if (sc->hn_nvs_ver <= HN_NVS_VERSION_4)
573307199Ssephe				sc->hn_ndis_ver = HN_NDIS_VERSION_6_1;
574307200Ssephe
575307174Ssephe			if (bootverbose) {
576307181Ssephe				if_printf(sc->hn_ifp, "NVS version 0x%x, "
577307200Ssephe				    "NDIS version %u.%u\n", sc->hn_nvs_ver,
578307199Ssephe				    HN_NDIS_VERSION_MAJOR(sc->hn_ndis_ver),
579307199Ssephe				    HN_NDIS_VERSION_MINOR(sc->hn_ndis_ver));
580307174Ssephe			}
581308494Ssephe			goto done;
582250199Sgrehan		}
583250199Sgrehan	}
584307200Ssephe	if_printf(sc->hn_ifp, "no NVS available\n");
585307200Ssephe	return (ENXIO);
586308494Ssephe
587308494Ssephedone:
588308494Ssephe	if (sc->hn_nvs_ver >= HN_NVS_VERSION_5)
589308494Ssephe		sc->hn_caps |= HN_CAP_HASHVAL;
590308494Ssephe	return (0);
591307200Ssephe}
592250199Sgrehan
593307206Ssepheint
594307206Ssephehn_nvs_attach(struct hn_softc *sc, int mtu)
595307200Ssephe{
596307206Ssephe	int error;
597285236Swhu
598307202Ssephe	/*
599307202Ssephe	 * Initialize NVS.
600307202Ssephe	 */
601307206Ssephe	error = hn_nvs_init(sc);
602307206Ssephe	if (error)
603307206Ssephe		return (error);
604307200Ssephe
605307202Ssephe	if (sc->hn_nvs_ver >= HN_NVS_VERSION_2) {
606307202Ssephe		/*
607307202Ssephe		 * Configure NDIS before initializing it.
608307202Ssephe		 */
609307206Ssephe		error = hn_nvs_conf_ndis(sc, mtu);
610307206Ssephe		if (error)
611307206Ssephe			return (error);
612307202Ssephe	}
613250199Sgrehan
614250199Sgrehan	/*
615307164Ssephe	 * Initialize NDIS.
616250199Sgrehan	 */
617307206Ssephe	error = hn_nvs_init_ndis(sc);
618307206Ssephe	if (error)
619307206Ssephe		return (error);
620250199Sgrehan
621307206Ssephe	/*
622307206Ssephe	 * Connect RXBUF.
623307206Ssephe	 */
624307206Ssephe	error = hn_nvs_conn_rxbuf(sc);
625307206Ssephe	if (error)
626307206Ssephe		return (error);
627307206Ssephe
628307206Ssephe	/*
629307206Ssephe	 * Connect chimney sending buffer.
630307206Ssephe	 */
631307206Ssephe	error = hn_nvs_conn_chim(sc);
632310757Ssephe	if (error) {
633310757Ssephe		hn_nvs_disconn_rxbuf(sc);
634307206Ssephe		return (error);
635310757Ssephe	}
636307206Ssephe	return (0);
637250199Sgrehan}
638250199Sgrehan
639307250Ssephevoid
640307250Ssephehn_nvs_detach(struct hn_softc *sc)
641250199Sgrehan{
642307250Ssephe
643307250Ssephe	/* NOTE: there are no requests to stop the NVS. */
644307206Ssephe	hn_nvs_disconn_rxbuf(sc);
645307206Ssephe	hn_nvs_disconn_chim(sc);
646250199Sgrehan}
647250199Sgrehan
648307185Ssephevoid
649308504Ssephehn_nvs_sent_xact(struct hn_nvs_sendctx *sndc,
650307174Ssephe    struct hn_softc *sc __unused, struct vmbus_channel *chan __unused,
651307167Ssephe    const void *data, int dlen)
652307161Ssephe{
653307164Ssephe
654307167Ssephe	vmbus_xact_wakeup(sndc->hn_cbarg, data, dlen);
655307161Ssephe}
656307161Ssephe
657307161Ssephestatic void
658308504Ssephehn_nvs_sent_none(struct hn_nvs_sendctx *sndc __unused,
659307174Ssephe    struct hn_softc *sc __unused, struct vmbus_channel *chan __unused,
660307167Ssephe    const void *data __unused, int dlen __unused)
661307161Ssephe{
662307161Ssephe	/* EMPTY */
663307161Ssephe}
664307161Ssephe
665250199Sgrehanint
666307206Ssephehn_nvs_alloc_subchans(struct hn_softc *sc, int *nsubch0)
667307206Ssephe{
668307206Ssephe	struct vmbus_xact *xact;
669307206Ssephe	struct hn_nvs_subch_req *req;
670307206Ssephe	const struct hn_nvs_subch_resp *resp;
671307206Ssephe	int error, nsubch_req;
672307206Ssephe	uint32_t nsubch;
673307206Ssephe	size_t resp_len;
674307206Ssephe
675307206Ssephe	nsubch_req = *nsubch0;
676307206Ssephe	KASSERT(nsubch_req > 0, ("invalid # of sub-channels %d", nsubch_req));
677307206Ssephe
678307206Ssephe	xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
679307206Ssephe	if (xact == NULL) {
680307206Ssephe		if_printf(sc->hn_ifp, "no xact for nvs subch alloc\n");
681307206Ssephe		return (ENXIO);
682307206Ssephe	}
683307206Ssephe	req = vmbus_xact_req_data(xact);
684307206Ssephe	req->nvs_type = HN_NVS_TYPE_SUBCH_REQ;
685307206Ssephe	req->nvs_op = HN_NVS_SUBCH_OP_ALLOC;
686307206Ssephe	req->nvs_nsubch = nsubch_req;
687307206Ssephe
688307206Ssephe	resp_len = sizeof(*resp);
689307206Ssephe	resp = hn_nvs_xact_execute(sc, xact, req, sizeof(*req), &resp_len,
690307206Ssephe	    HN_NVS_TYPE_SUBCH_RESP);
691307206Ssephe	if (resp == NULL) {
692307206Ssephe		if_printf(sc->hn_ifp, "exec nvs subch alloc failed\n");
693307206Ssephe		error = EIO;
694307206Ssephe		goto done;
695307206Ssephe	}
696307206Ssephe	if (resp->nvs_status != HN_NVS_STATUS_OK) {
697307206Ssephe		if_printf(sc->hn_ifp, "nvs subch alloc failed: %x\n",
698307206Ssephe		    resp->nvs_status);
699307206Ssephe		error = EIO;
700307206Ssephe		goto done;
701307206Ssephe	}
702307206Ssephe
703307206Ssephe	nsubch = resp->nvs_nsubch;
704307206Ssephe	if (nsubch > nsubch_req) {
705307206Ssephe		if_printf(sc->hn_ifp, "%u subchans are allocated, "
706307206Ssephe		    "requested %d\n", nsubch, nsubch_req);
707307206Ssephe		nsubch = nsubch_req;
708307206Ssephe	}
709307206Ssephe	*nsubch0 = nsubch;
710307206Ssephe	error = 0;
711307206Ssephedone:
712307206Ssephe	vmbus_xact_put(xact);
713307206Ssephe	return (error);
714307206Ssephe}
715308504Ssephe
716308504Ssepheint
717308504Ssephehn_nvs_send_rndis_ctrl(struct vmbus_channel *chan,
718308504Ssephe    struct hn_nvs_sendctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt)
719308504Ssephe{
720308504Ssephe
721308504Ssephe	return hn_nvs_send_rndis_sglist(chan, HN_NVS_RNDIS_MTYPE_CTRL,
722308504Ssephe	    sndc, gpa, gpa_cnt);
723308504Ssephe}
724314123Sdexuan
725314123Sdexuanvoid
726314123Sdexuanhn_nvs_set_datapath(struct hn_softc *sc, uint32_t path)
727314123Sdexuan{
728314123Sdexuan	struct hn_nvs_datapath dp;
729314123Sdexuan
730314123Sdexuan	memset(&dp, 0, sizeof(dp));
731314123Sdexuan	dp.nvs_type = HN_NVS_TYPE_SET_DATAPATH;
732314123Sdexuan	dp.nvs_active_path = path;
733314123Sdexuan
734314123Sdexuan	hn_nvs_req_send(sc, &dp, sizeof(dp));
735314123Sdexuan}
736