vmbus_ic.c revision 307845
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 * $FreeBSD: head/sys/dev/hyperv/utilities/hv_util.c 307845 2016-10-24 05:36:19Z sephe $
27 */
28
29/*
30 * A common driver for all hyper-V util services.
31 */
32
33#include <sys/param.h>
34#include <sys/kernel.h>
35#include <sys/bus.h>
36#include <sys/malloc.h>
37#include <sys/module.h>
38#include <sys/reboot.h>
39#include <sys/systm.h>
40#include <sys/sysctl.h>
41#include <sys/timetc.h>
42
43#include <dev/hyperv/include/hyperv.h>
44#include <dev/hyperv/include/vmbus.h>
45#include <dev/hyperv/utilities/hv_util.h>
46#include <dev/hyperv/utilities/vmbus_icreg.h>
47
48#include "vmbus_if.h"
49
50#define VMBUS_IC_BRSIZE		(4 * PAGE_SIZE)
51
52#define VMBUS_IC_VERCNT		2
53#define VMBUS_IC_NEGOSZ		\
54	__offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT])
55CTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE);
56
57static int	vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS);
58static int	vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS);
59
60int
61vmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen0,
62    uint32_t fw_ver, uint32_t msg_ver)
63{
64	struct vmbus_icmsg_negotiate *nego;
65	int i, cnt, dlen = *dlen0, error;
66	uint32_t sel_fw_ver, sel_msg_ver;
67	bool has_fw_ver, has_msg_ver;
68
69	/*
70	 * Preliminary message verification.
71	 */
72	if (dlen < sizeof(*nego)) {
73		device_printf(sc->ic_dev, "truncated ic negotiate, len %d\n",
74		    dlen);
75		return (EINVAL);
76	}
77	nego = data;
78
79	if (nego->ic_fwver_cnt == 0) {
80		device_printf(sc->ic_dev, "ic negotiate does not contain "
81		    "framework version %u\n", nego->ic_fwver_cnt);
82		return (EINVAL);
83	}
84	if (nego->ic_msgver_cnt == 0) {
85		device_printf(sc->ic_dev, "ic negotiate does not contain "
86		    "message version %u\n", nego->ic_msgver_cnt);
87		return (EINVAL);
88	}
89
90	cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt;
91	if (dlen < __offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) {
92		device_printf(sc->ic_dev, "ic negotiate does not contain "
93		    "versions %d\n", dlen);
94		return (EINVAL);
95	}
96
97	error = EOPNOTSUPP;
98
99	/*
100	 * Find the best match framework version.
101	 */
102	has_fw_ver = false;
103	for (i = 0; i < nego->ic_fwver_cnt; ++i) {
104		if (VMBUS_ICVER_LE(nego->ic_ver[i], fw_ver)) {
105			if (!has_fw_ver) {
106				sel_fw_ver = nego->ic_ver[i];
107				has_fw_ver = true;
108			} else if (VMBUS_ICVER_GT(nego->ic_ver[i],
109			    sel_fw_ver)) {
110				sel_fw_ver = nego->ic_ver[i];
111			}
112		}
113	}
114	if (!has_fw_ver) {
115		device_printf(sc->ic_dev, "failed to select framework "
116		    "version\n");
117		goto done;
118	}
119
120	/*
121	 * Fine the best match message version.
122	 */
123	has_msg_ver = false;
124	for (i = nego->ic_fwver_cnt;
125	    i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; ++i) {
126		if (VMBUS_ICVER_LE(nego->ic_ver[i], msg_ver)) {
127			if (!has_msg_ver) {
128				sel_msg_ver = nego->ic_ver[i];
129				has_msg_ver = true;
130			} else if (VMBUS_ICVER_GT(nego->ic_ver[i],
131			    sel_msg_ver)) {
132				sel_msg_ver = nego->ic_ver[i];
133			}
134		}
135	}
136	if (!has_msg_ver) {
137		device_printf(sc->ic_dev, "failed to select message "
138		    "version\n");
139		goto done;
140	}
141
142	error = 0;
143done:
144	if (bootverbose || !has_fw_ver || !has_msg_ver) {
145		if (has_fw_ver) {
146			device_printf(sc->ic_dev, "sel framework version: "
147			    "%u.%u\n",
148			    VMBUS_ICVER_MAJOR(sel_fw_ver),
149			    VMBUS_ICVER_MINOR(sel_fw_ver));
150		}
151		for (i = 0; i < nego->ic_fwver_cnt; i++) {
152			device_printf(sc->ic_dev, "supp framework version: "
153			    "%u.%u\n",
154			    VMBUS_ICVER_MAJOR(nego->ic_ver[i]),
155			    VMBUS_ICVER_MINOR(nego->ic_ver[i]));
156		}
157
158		if (has_msg_ver) {
159			device_printf(sc->ic_dev, "sel message version: "
160			    "%u.%u\n",
161			    VMBUS_ICVER_MAJOR(sel_msg_ver),
162			    VMBUS_ICVER_MINOR(sel_msg_ver));
163		}
164		for (i = nego->ic_fwver_cnt;
165		    i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; i++) {
166			device_printf(sc->ic_dev, "supp message version: "
167			    "%u.%u\n",
168			    VMBUS_ICVER_MAJOR(nego->ic_ver[i]),
169			    VMBUS_ICVER_MINOR(nego->ic_ver[i]));
170		}
171	}
172	if (error)
173		return (error);
174
175	/* Record the selected versions. */
176	sc->ic_fwver = sel_fw_ver;
177	sc->ic_msgver = sel_msg_ver;
178
179	/* One framework version. */
180	nego->ic_fwver_cnt = 1;
181	nego->ic_ver[0] = sel_fw_ver;
182
183	/* One message version. */
184	nego->ic_msgver_cnt = 1;
185	nego->ic_ver[1] = sel_msg_ver;
186
187	/* Update data size. */
188	nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ -
189	    sizeof(struct vmbus_icmsg_hdr);
190
191	/* Update total size, if necessary. */
192	if (dlen < VMBUS_IC_NEGOSZ)
193		*dlen0 = VMBUS_IC_NEGOSZ;
194
195	return (0);
196}
197
198int
199vmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[])
200{
201	device_t bus = device_get_parent(dev);
202	const struct vmbus_ic_desc *d;
203
204	if (resource_disabled(device_get_name(dev), 0))
205		return (ENXIO);
206
207	for (d = descs; d->ic_desc != NULL; ++d) {
208		if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) {
209			device_set_desc(dev, d->ic_desc);
210			return (BUS_PROBE_DEFAULT);
211		}
212	}
213	return (ENXIO);
214}
215
216int
217hv_util_attach(device_t dev, vmbus_chan_callback_t cb)
218{
219	struct hv_util_sc *sc = device_get_softc(dev);
220	struct vmbus_channel *chan = vmbus_get_channel(dev);
221	struct sysctl_oid_list *child;
222	struct sysctl_ctx_list *ctx;
223	int error;
224
225	sc->ic_dev = dev;
226	sc->ic_buflen = VMBUS_IC_BRSIZE;
227	sc->receive_buffer = malloc(VMBUS_IC_BRSIZE, M_DEVBUF,
228	    M_WAITOK | M_ZERO);
229
230	/*
231	 * These services are not performance critical and do not need
232	 * batched reading. Furthermore, some services such as KVP can
233	 * only handle one message from the host at a time.
234	 * Turn off batched reading for all util drivers before we open the
235	 * channel.
236	 */
237	vmbus_chan_set_readbatch(chan, false);
238
239	error = vmbus_chan_open(chan, VMBUS_IC_BRSIZE, VMBUS_IC_BRSIZE, NULL, 0,
240	    cb, sc);
241	if (error) {
242		free(sc->receive_buffer, M_DEVBUF);
243		return (error);
244	}
245
246	ctx = device_get_sysctl_ctx(dev);
247	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
248	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "fw_version",
249	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
250	    vmbus_ic_fwver_sysctl, "A", "framework version");
251	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "msg_version",
252	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
253	    vmbus_ic_msgver_sysctl, "A", "message version");
254
255	return (0);
256}
257
258static int
259vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS)
260{
261	struct hv_util_sc *sc = arg1;
262	char verstr[16];
263
264	snprintf(verstr, sizeof(verstr), "%u.%u",
265	    VMBUS_ICVER_MAJOR(sc->ic_fwver), VMBUS_ICVER_MINOR(sc->ic_fwver));
266	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
267}
268
269static int
270vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS)
271{
272	struct hv_util_sc *sc = arg1;
273	char verstr[16];
274
275	snprintf(verstr, sizeof(verstr), "%u.%u",
276	    VMBUS_ICVER_MAJOR(sc->ic_msgver), VMBUS_ICVER_MINOR(sc->ic_msgver));
277	return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
278}
279
280int
281hv_util_detach(device_t dev)
282{
283	struct hv_util_sc *sc = device_get_softc(dev);
284
285	vmbus_chan_close(vmbus_get_channel(dev));
286	free(sc->receive_buffer, M_DEVBUF);
287
288	return (0);
289}
290