vmbus_ic.c revision 304788
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 304788 2016-08-25 05:24:57Z 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/timetc.h>
41
42#include <dev/hyperv/include/hyperv.h>
43#include <dev/hyperv/include/vmbus.h>
44#include <dev/hyperv/utilities/hv_util.h>
45#include <dev/hyperv/utilities/vmbus_icreg.h>
46
47#include "vmbus_if.h"
48
49#define VMBUS_IC_BRSIZE		(4 * PAGE_SIZE)
50
51#define VMBUS_IC_VERCNT		2
52#define VMBUS_IC_NEGOSZ		\
53	__offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT])
54CTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE);
55
56int
57vmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen0)
58{
59	struct vmbus_icmsg_negotiate *nego;
60	int cnt, major, dlen = *dlen0;
61
62	/*
63	 * Preliminary message size 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	cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt;
73	if (dlen < __offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) {
74		device_printf(sc->ic_dev, "ic negotiate does not contain "
75		    "versions %d\n", dlen);
76		return EINVAL;
77	}
78
79	/* Select major version; XXX looks wrong. */
80	if (nego->ic_fwver_cnt >= 2 && VMBUS_ICVER_MAJOR(nego->ic_ver[1]) == 3)
81		major = 3;
82	else
83		major = 1;
84
85	/* One framework version */
86	nego->ic_fwver_cnt = 1;
87	nego->ic_ver[0] = VMBUS_IC_VERSION(major, 0);
88
89	/* One message version */
90	nego->ic_msgver_cnt = 1;
91	nego->ic_ver[1] = VMBUS_IC_VERSION(major, 0);
92
93	/* Update data size */
94	nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ -
95	    sizeof(struct vmbus_icmsg_hdr);
96
97	/* Update total size, if necessary */
98	if (dlen < VMBUS_IC_NEGOSZ)
99		*dlen0 = VMBUS_IC_NEGOSZ;
100
101	return 0;
102}
103
104int
105vmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[])
106{
107	device_t bus = device_get_parent(dev);
108	const struct vmbus_ic_desc *d;
109
110	if (resource_disabled(device_get_name(dev), 0))
111		return (ENXIO);
112
113	for (d = descs; d->ic_desc != NULL; ++d) {
114		if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) {
115			device_set_desc(dev, d->ic_desc);
116			return (BUS_PROBE_DEFAULT);
117		}
118	}
119	return (ENXIO);
120}
121
122int
123hv_util_attach(device_t dev, vmbus_chan_callback_t cb)
124{
125	struct hv_util_sc *sc = device_get_softc(dev);
126	struct vmbus_channel *chan = vmbus_get_channel(dev);
127	int error;
128
129	sc->ic_dev = dev;
130	sc->ic_buflen = VMBUS_IC_BRSIZE;
131	sc->receive_buffer = malloc(VMBUS_IC_BRSIZE, M_DEVBUF,
132	    M_WAITOK | M_ZERO);
133
134	/*
135	 * These services are not performance critical and do not need
136	 * batched reading. Furthermore, some services such as KVP can
137	 * only handle one message from the host at a time.
138	 * Turn off batched reading for all util drivers before we open the
139	 * channel.
140	 */
141	vmbus_chan_set_readbatch(chan, false);
142
143	error = vmbus_chan_open(chan, VMBUS_IC_BRSIZE, VMBUS_IC_BRSIZE, NULL, 0,
144	    cb, sc);
145	if (error) {
146		free(sc->receive_buffer, M_DEVBUF);
147		return (error);
148	}
149	return (0);
150}
151
152int
153hv_util_detach(device_t dev)
154{
155	struct hv_util_sc *sc = device_get_softc(dev);
156
157	vmbus_chan_close(vmbus_get_channel(dev));
158	free(sc->receive_buffer, M_DEVBUF);
159
160	return (0);
161}
162