1/*-
2 * Copyright (c) 2014,2016 Microsoft Corp.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/param.h>
28#include <sys/bus.h>
29#include <sys/malloc.h>
30#include <sys/systm.h>
31#include <sys/sysctl.h>
32
33#include <dev/hyperv/include/hyperv.h>
34#include <dev/hyperv/include/vmbus.h>
35#include <dev/hyperv/utilities/vmbus_icreg.h>
36#include <dev/hyperv/utilities/vmbus_icvar.h>
37
38#include "vmbus_if.h"
39
40#define VMBUS_IC_BRSIZE		(4 * PAGE_SIZE)
41
42#define VMBUS_IC_VERCNT		2
43#define VMBUS_IC_NEGOSZ		\
44	__offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT])
45CTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE);
46
47static int	vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS);
48static int	vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS);
49
50int
51vmbus_ic_negomsg(struct vmbus_ic_softc *sc, void *data, int *dlen0,
52    uint32_t fw_ver, uint32_t msg_ver)
53{
54	struct vmbus_icmsg_negotiate *nego;
55	int i, cnt, dlen = *dlen0, error;
56	uint32_t sel_fw_ver, sel_msg_ver;
57	bool has_fw_ver, has_msg_ver;
58
59	has_fw_ver = false;
60	has_msg_ver = false;
61
62	/*
63	 * Preliminary message verification.
64	 */
65	if (dlen < sizeof(*nego)) {
66		device_printf(sc->ic_dev, "truncated ic negotiate, len %d\n",
67		    dlen);
68		return (EINVAL);
69	}
70	nego = data;
71
72	if (nego->ic_fwver_cnt == 0) {
73		device_printf(sc->ic_dev, "ic negotiate does not contain "
74		    "framework version %u\n", nego->ic_fwver_cnt);
75		return (EINVAL);
76	}
77	if (nego->ic_msgver_cnt == 0) {
78		device_printf(sc->ic_dev, "ic negotiate does not contain "
79		    "message version %u\n", nego->ic_msgver_cnt);
80		return (EINVAL);
81	}
82
83	cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt;
84	if (dlen < __offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) {
85		device_printf(sc->ic_dev, "ic negotiate does not contain "
86		    "versions %d\n", dlen);
87		return (EINVAL);
88	}
89
90	error = EOPNOTSUPP;
91
92	/*
93	 * Find the best match framework version.
94	 */
95	for (i = 0; i < nego->ic_fwver_cnt; ++i) {
96		if (VMBUS_ICVER_LE(nego->ic_ver[i], fw_ver)) {
97			if (!has_fw_ver) {
98				sel_fw_ver = nego->ic_ver[i];
99				has_fw_ver = true;
100			} else if (VMBUS_ICVER_GT(nego->ic_ver[i],
101			    sel_fw_ver)) {
102				sel_fw_ver = nego->ic_ver[i];
103			}
104		}
105	}
106	if (!has_fw_ver) {
107		device_printf(sc->ic_dev, "failed to select framework "
108		    "version\n");
109		goto done;
110	}
111
112	/*
113	 * Find the best match message version.
114	 */
115	for (i = nego->ic_fwver_cnt;
116	    i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; ++i) {
117		if (VMBUS_ICVER_LE(nego->ic_ver[i], msg_ver)) {
118			if (!has_msg_ver) {
119				sel_msg_ver = nego->ic_ver[i];
120				has_msg_ver = true;
121			} else if (VMBUS_ICVER_GT(nego->ic_ver[i],
122			    sel_msg_ver)) {
123				sel_msg_ver = nego->ic_ver[i];
124			}
125		}
126	}
127	if (!has_msg_ver) {
128		device_printf(sc->ic_dev, "failed to select message "
129		    "version\n");
130		goto done;
131	}
132
133	error = 0;
134done:
135	if (bootverbose || !has_fw_ver || !has_msg_ver) {
136		if (has_fw_ver) {
137			device_printf(sc->ic_dev, "sel framework version: "
138			    "%u.%u\n",
139			    VMBUS_ICVER_MAJOR(sel_fw_ver),
140			    VMBUS_ICVER_MINOR(sel_fw_ver));
141		}
142		for (i = 0; i < nego->ic_fwver_cnt; i++) {
143			device_printf(sc->ic_dev, "supp framework version: "
144			    "%u.%u\n",
145			    VMBUS_ICVER_MAJOR(nego->ic_ver[i]),
146			    VMBUS_ICVER_MINOR(nego->ic_ver[i]));
147		}
148
149		if (has_msg_ver) {
150			device_printf(sc->ic_dev, "sel message version: "
151			    "%u.%u\n",
152			    VMBUS_ICVER_MAJOR(sel_msg_ver),
153			    VMBUS_ICVER_MINOR(sel_msg_ver));
154		}
155		for (i = nego->ic_fwver_cnt;
156		    i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; i++) {
157			device_printf(sc->ic_dev, "supp message version: "
158			    "%u.%u\n",
159			    VMBUS_ICVER_MAJOR(nego->ic_ver[i]),
160			    VMBUS_ICVER_MINOR(nego->ic_ver[i]));
161		}
162	}
163	if (error)
164		return (error);
165
166	/* Record the selected versions. */
167	sc->ic_fwver = sel_fw_ver;
168	sc->ic_msgver = sel_msg_ver;
169
170	/* One framework version. */
171	nego->ic_fwver_cnt = 1;
172	nego->ic_ver[0] = sel_fw_ver;
173
174	/* One message version. */
175	nego->ic_msgver_cnt = 1;
176	nego->ic_ver[1] = sel_msg_ver;
177
178	/* Update data size. */
179	nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ -
180	    sizeof(struct vmbus_icmsg_hdr);
181
182	/* Update total size, if necessary. */
183	if (dlen < VMBUS_IC_NEGOSZ)
184		*dlen0 = VMBUS_IC_NEGOSZ;
185
186	return (0);
187}
188
189int
190vmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[])
191{
192	device_t bus = device_get_parent(dev);
193	const struct vmbus_ic_desc *d;
194
195	if (resource_disabled(device_get_name(dev), 0))
196		return (ENXIO);
197
198	for (d = descs; d->ic_desc != NULL; ++d) {
199		if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) {
200			device_set_desc(dev, d->ic_desc);
201			return (BUS_PROBE_DEFAULT);
202		}
203	}
204	return (ENXIO);
205}
206
207int
208vmbus_ic_attach(device_t dev, vmbus_chan_callback_t cb)
209{
210	struct vmbus_ic_softc *sc = device_get_softc(dev);
211	struct vmbus_channel *chan = vmbus_get_channel(dev);
212	struct sysctl_oid_list *child;
213	struct sysctl_ctx_list *ctx;
214	int error;
215
216	sc->ic_dev = dev;
217	sc->ic_buflen = VMBUS_IC_BRSIZE;
218	sc->ic_buf = malloc(VMBUS_IC_BRSIZE, M_DEVBUF, M_WAITOK | M_ZERO);
219
220	/*
221	 * These services are not performance critical and do not need
222	 * batched reading. Furthermore, some services such as KVP can
223	 * only handle one message from the host at a time.
224	 * Turn off batched reading for all util drivers before we open the
225	 * channel.
226	 */
227	vmbus_chan_set_readbatch(chan, false);
228
229	error = vmbus_chan_open(chan, VMBUS_IC_BRSIZE, VMBUS_IC_BRSIZE, NULL, 0,
230	    cb, sc);
231	if (error) {
232		free(sc->ic_buf, M_DEVBUF);
233		return (error);
234	}
235
236	ctx = device_get_sysctl_ctx(dev);
237	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
238	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "fw_version",
239	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
240	    vmbus_ic_fwver_sysctl, "A", "framework version");
241	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "msg_version",
242	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
243	    vmbus_ic_msgver_sysctl, "A", "message version");
244
245	return (0);
246}
247
248static int
249vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS)
250{
251	struct vmbus_ic_softc *sc = arg1;
252	char verstr[16];
253
254	snprintf(verstr, sizeof(verstr), "%u.%u",
255	    VMBUS_ICVER_MAJOR(sc->ic_fwver), VMBUS_ICVER_MINOR(sc->ic_fwver));
256	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
257}
258
259static int
260vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS)
261{
262	struct vmbus_ic_softc *sc = arg1;
263	char verstr[16];
264
265	snprintf(verstr, sizeof(verstr), "%u.%u",
266	    VMBUS_ICVER_MAJOR(sc->ic_msgver), VMBUS_ICVER_MINOR(sc->ic_msgver));
267	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
268}
269
270int
271vmbus_ic_detach(device_t dev)
272{
273	struct vmbus_ic_softc *sc = device_get_softc(dev);
274
275	vmbus_chan_close(vmbus_get_channel(dev));
276	free(sc->ic_buf, M_DEVBUF);
277
278	return (0);
279}
280
281int
282vmbus_ic_sendresp(struct vmbus_ic_softc *sc, struct vmbus_channel *chan,
283    void *data, int dlen, uint64_t xactid)
284{
285	struct vmbus_icmsg_hdr *hdr;
286	int error;
287
288	KASSERT(dlen >= sizeof(*hdr), ("invalid data length %d", dlen));
289	hdr = data;
290
291	hdr->ic_flags = VMBUS_ICMSG_FLAG_XACT | VMBUS_ICMSG_FLAG_RESP;
292	error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_INBAND, 0,
293	    data, dlen, xactid);
294	if (error)
295		device_printf(sc->ic_dev, "resp send failed: %d\n", error);
296	return (error);
297}
298