xref: /netbsd-src/sys/dev/hyperv/vmbusic.c (revision 02991323a4c6327cf82e06cbf1e20d61b5c66424)
1*02991323Schs /*	$NetBSD: vmbusic.c,v 1.2 2019/10/01 18:00:08 chs Exp $	*/
250517e57Snonaka /*-
350517e57Snonaka  * Copyright (c) 2014,2016 Microsoft Corp.
450517e57Snonaka  * All rights reserved.
550517e57Snonaka  *
650517e57Snonaka  * Redistribution and use in source and binary forms, with or without
750517e57Snonaka  * modification, are permitted provided that the following conditions
850517e57Snonaka  * are met:
950517e57Snonaka  * 1. Redistributions of source code must retain the above copyright
1050517e57Snonaka  *    notice unmodified, this list of conditions, and the following
1150517e57Snonaka  *    disclaimer.
1250517e57Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
1350517e57Snonaka  *    notice, this list of conditions and the following disclaimer in the
1450517e57Snonaka  *    documentation and/or other materials provided with the distribution.
1550517e57Snonaka  *
1650517e57Snonaka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1750517e57Snonaka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1850517e57Snonaka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1950517e57Snonaka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2050517e57Snonaka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2150517e57Snonaka  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2250517e57Snonaka  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2350517e57Snonaka  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2450517e57Snonaka  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2550517e57Snonaka  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2650517e57Snonaka  */
2750517e57Snonaka 
2850517e57Snonaka #include <sys/cdefs.h>
2950517e57Snonaka #ifdef __KERNEL_RCSID
30*02991323Schs __KERNEL_RCSID(0, "$NetBSD: vmbusic.c,v 1.2 2019/10/01 18:00:08 chs Exp $");
3150517e57Snonaka #endif
3250517e57Snonaka #ifdef __FBSDID
3350517e57Snonaka __FBSDID("$FreeBSD: head/sys/dev/hyperv/utilities/vmbus_ic.c 310317 2016-12-20 07:14:24Z sephe $");
3450517e57Snonaka #endif
3550517e57Snonaka 
3650517e57Snonaka #include <sys/param.h>
3750517e57Snonaka #include <sys/systm.h>
3850517e57Snonaka #include <sys/kernel.h>
3950517e57Snonaka #include <sys/device.h>
4050517e57Snonaka #include <sys/kmem.h>
4150517e57Snonaka #include <sys/reboot.h>
4250517e57Snonaka 
4350517e57Snonaka #include <dev/hyperv/vmbusvar.h>
4450517e57Snonaka #include <dev/hyperv/vmbusicreg.h>
4550517e57Snonaka #include <dev/hyperv/vmbusicvar.h>
4650517e57Snonaka 
4750517e57Snonaka #define VMBUS_IC_BRSIZE		(4 * PAGE_SIZE)
4850517e57Snonaka 
4950517e57Snonaka #define VMBUS_IC_VERCNT         2
5050517e57Snonaka #define VMBUS_IC_NEGOSZ		\
5150517e57Snonaka     offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT])
5250517e57Snonaka __CTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE);
5350517e57Snonaka 
5450517e57Snonaka 
5550517e57Snonaka int
vmbusic_probe(struct vmbus_attach_args * aa,const struct hyperv_guid * guid)5650517e57Snonaka vmbusic_probe(struct vmbus_attach_args *aa, const struct hyperv_guid *guid)
5750517e57Snonaka {
5850517e57Snonaka 
5950517e57Snonaka 	if (memcmp(aa->aa_type, guid, sizeof(*aa->aa_type)) != 0)
6050517e57Snonaka 		return 0;
6150517e57Snonaka 	return 1;
6250517e57Snonaka }
6350517e57Snonaka 
6450517e57Snonaka int
vmbusic_attach(device_t dv,struct vmbus_attach_args * aa,vmbus_channel_callback_t cb)6550517e57Snonaka vmbusic_attach(device_t dv, struct vmbus_attach_args *aa,
6650517e57Snonaka     vmbus_channel_callback_t cb)
6750517e57Snonaka {
6850517e57Snonaka 	struct vmbusic_softc *sc = device_private(dv);
6950517e57Snonaka 
7050517e57Snonaka 	sc->sc_dev = dv;
7150517e57Snonaka 	sc->sc_chan = aa->aa_chan;
7250517e57Snonaka 
7350517e57Snonaka 	sc->sc_buflen = VMBUS_IC_BRSIZE;
74*02991323Schs 	sc->sc_buf = kmem_alloc(sc->sc_buflen, KM_SLEEP);
7550517e57Snonaka 
7650517e57Snonaka 	/*
7750517e57Snonaka 	 * These services are not performance critical and do not need
7850517e57Snonaka 	 * batched reading. Furthermore, some services such as KVP can
7950517e57Snonaka 	 * only handle one message from the host at a time.
8050517e57Snonaka 	 * Turn off batched reading for all util drivers before we open the
8150517e57Snonaka 	 * channel.
8250517e57Snonaka 	 */
8350517e57Snonaka 	sc->sc_chan->ch_flags &= ~CHF_BATCHED;
8450517e57Snonaka 
8550517e57Snonaka 	if (vmbus_channel_open(sc->sc_chan, sc->sc_buflen, NULL, 0, cb, sc)) {
8650517e57Snonaka 		aprint_error_dev(dv, "failed to open channel\n");
8750517e57Snonaka 		kmem_free(sc->sc_buf, sc->sc_buflen);
8850517e57Snonaka 		sc->sc_buf = NULL;
8950517e57Snonaka 		return ENXIO;
9050517e57Snonaka 	}
9150517e57Snonaka 
9250517e57Snonaka 	return 0;
9350517e57Snonaka }
9450517e57Snonaka 
9550517e57Snonaka int
vmbusic_detach(device_t dv,int flags)9650517e57Snonaka vmbusic_detach(device_t dv, int flags)
9750517e57Snonaka {
9850517e57Snonaka 	struct vmbusic_softc *sc = device_private(dv);
9950517e57Snonaka 	int error;
10050517e57Snonaka 
10150517e57Snonaka 	error = vmbus_channel_close(sc->sc_chan);
10250517e57Snonaka 	if (error != 0)
10350517e57Snonaka 		return error;
10450517e57Snonaka 
10550517e57Snonaka 	if (sc->sc_buf != NULL) {
10650517e57Snonaka 		kmem_free(sc->sc_buf, sc->sc_buflen);
10750517e57Snonaka 		sc->sc_buf = NULL;
10850517e57Snonaka 	}
10950517e57Snonaka 
11050517e57Snonaka 	if (sc->sc_log != NULL) {
11150517e57Snonaka 		sysctl_teardown(&sc->sc_log);
11250517e57Snonaka 		sc->sc_log = NULL;
11350517e57Snonaka 	}
11450517e57Snonaka 
11550517e57Snonaka 	return 0;
11650517e57Snonaka }
11750517e57Snonaka 
11850517e57Snonaka int
vmbusic_negotiate(struct vmbusic_softc * sc,void * data,uint32_t * dlen0,uint32_t fw_ver,uint32_t msg_ver)11950517e57Snonaka vmbusic_negotiate(struct vmbusic_softc *sc, void *data, uint32_t *dlen0,
12050517e57Snonaka     uint32_t fw_ver, uint32_t msg_ver)
12150517e57Snonaka {
12250517e57Snonaka 	struct vmbus_icmsg_negotiate *nego;
12350517e57Snonaka 	uint32_t sel_fw_ver = 0, sel_msg_ver = 0;
12450517e57Snonaka 	int i, cnt, dlen = *dlen0, error;
12550517e57Snonaka 	bool has_fw_ver, has_msg_ver = false;
12650517e57Snonaka 
12750517e57Snonaka 	/*
12850517e57Snonaka 	 * Preliminary message verification.
12950517e57Snonaka 	 */
13050517e57Snonaka 	if (dlen < sizeof(*nego)) {
13150517e57Snonaka 		device_printf(sc->sc_dev, "truncated ic negotiate, len %d\n",
13250517e57Snonaka 		    dlen);
13350517e57Snonaka 		return EINVAL;
13450517e57Snonaka 	}
13550517e57Snonaka 	nego = data;
13650517e57Snonaka 
13750517e57Snonaka 	if (nego->ic_fwver_cnt == 0) {
13850517e57Snonaka 		device_printf(sc->sc_dev, "ic negotiate does not contain "
13950517e57Snonaka 		    "framework version %u\n", nego->ic_fwver_cnt);
14050517e57Snonaka 		return EINVAL;
14150517e57Snonaka 	}
14250517e57Snonaka 	if (nego->ic_msgver_cnt == 0) {
14350517e57Snonaka 		device_printf(sc->sc_dev, "ic negotiate does not contain "
14450517e57Snonaka 		    "message version %u\n", nego->ic_msgver_cnt);
14550517e57Snonaka 		return EINVAL;
14650517e57Snonaka 	}
14750517e57Snonaka 
14850517e57Snonaka 	cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt;
14950517e57Snonaka 	if (dlen < offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) {
15050517e57Snonaka 		device_printf(sc->sc_dev, "ic negotiate does not contain "
15150517e57Snonaka 		    "versions %d\n", dlen);
15250517e57Snonaka 		return EINVAL;
15350517e57Snonaka 	}
15450517e57Snonaka 
15550517e57Snonaka 	error = EOPNOTSUPP;
15650517e57Snonaka 
15750517e57Snonaka 	/*
15850517e57Snonaka 	 * Find the best match framework version.
15950517e57Snonaka 	 */
16050517e57Snonaka 	has_fw_ver = false;
16150517e57Snonaka 	for (i = 0; i < nego->ic_fwver_cnt; ++i) {
16250517e57Snonaka 		if (VMBUS_ICVER_LE(nego->ic_ver[i], fw_ver)) {
16350517e57Snonaka 			if (!has_fw_ver) {
16450517e57Snonaka 				sel_fw_ver = nego->ic_ver[i];
16550517e57Snonaka 				has_fw_ver = true;
16650517e57Snonaka 			} else if (VMBUS_ICVER_GT(nego->ic_ver[i],
16750517e57Snonaka 			    sel_fw_ver)) {
16850517e57Snonaka 				sel_fw_ver = nego->ic_ver[i];
16950517e57Snonaka 			}
17050517e57Snonaka 		}
17150517e57Snonaka 	}
17250517e57Snonaka 	if (!has_fw_ver) {
17350517e57Snonaka 		device_printf(sc->sc_dev, "failed to select framework "
17450517e57Snonaka 		    "version\n");
17550517e57Snonaka 		goto done;
17650517e57Snonaka 	}
17750517e57Snonaka 
17850517e57Snonaka 	/*
17950517e57Snonaka 	 * Fine the best match message version.
18050517e57Snonaka 	 */
18150517e57Snonaka 	has_msg_ver = false;
18250517e57Snonaka 	for (i = nego->ic_fwver_cnt;
18350517e57Snonaka 	    i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; ++i) {
18450517e57Snonaka 		if (VMBUS_ICVER_LE(nego->ic_ver[i], msg_ver)) {
18550517e57Snonaka 			if (!has_msg_ver) {
18650517e57Snonaka 				sel_msg_ver = nego->ic_ver[i];
18750517e57Snonaka 				has_msg_ver = true;
18850517e57Snonaka 			} else if (VMBUS_ICVER_GT(nego->ic_ver[i],
18950517e57Snonaka 			    sel_msg_ver)) {
19050517e57Snonaka 				sel_msg_ver = nego->ic_ver[i];
19150517e57Snonaka 			}
19250517e57Snonaka 		}
19350517e57Snonaka 	}
19450517e57Snonaka 	if (!has_msg_ver) {
19550517e57Snonaka 		device_printf(sc->sc_dev, "failed to select message version\n");
19650517e57Snonaka 		goto done;
19750517e57Snonaka 	}
19850517e57Snonaka 
19950517e57Snonaka 	error = 0;
20050517e57Snonaka done:
20150517e57Snonaka 	if (bootverbose || !has_fw_ver || !has_msg_ver) {
20250517e57Snonaka 		if (has_fw_ver) {
20350517e57Snonaka 			device_printf(sc->sc_dev,
20450517e57Snonaka 			    "sel framework version: %u.%u\n",
20550517e57Snonaka 			    VMBUS_ICVER_MAJOR(sel_fw_ver),
20650517e57Snonaka 			    VMBUS_ICVER_MINOR(sel_fw_ver));
20750517e57Snonaka 		}
20850517e57Snonaka 		for (i = 0; i < nego->ic_fwver_cnt; i++) {
20950517e57Snonaka 			device_printf(sc->sc_dev,
21050517e57Snonaka 			    "supp framework version: %u.%u\n",
21150517e57Snonaka 			    VMBUS_ICVER_MAJOR(nego->ic_ver[i]),
21250517e57Snonaka 			    VMBUS_ICVER_MINOR(nego->ic_ver[i]));
21350517e57Snonaka 		}
21450517e57Snonaka 
21550517e57Snonaka 		if (has_msg_ver) {
21650517e57Snonaka 			device_printf(sc->sc_dev,
21750517e57Snonaka 			    "sel message version: %u.%u\n",
21850517e57Snonaka 			    VMBUS_ICVER_MAJOR(sel_msg_ver),
21950517e57Snonaka 			    VMBUS_ICVER_MINOR(sel_msg_ver));
22050517e57Snonaka 		}
22150517e57Snonaka 		for (i = nego->ic_fwver_cnt;
22250517e57Snonaka 		    i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; i++) {
22350517e57Snonaka 			device_printf(sc->sc_dev,
22450517e57Snonaka 			    "supp message version: %u.%u\n",
22550517e57Snonaka 			    VMBUS_ICVER_MAJOR(nego->ic_ver[i]),
22650517e57Snonaka 			    VMBUS_ICVER_MINOR(nego->ic_ver[i]));
22750517e57Snonaka 		}
22850517e57Snonaka 	}
22950517e57Snonaka 	if (error)
23050517e57Snonaka 		return error;
23150517e57Snonaka 
23250517e57Snonaka 	/* Record the selected versions. */
23350517e57Snonaka 	sc->sc_fwver = sel_fw_ver;
23450517e57Snonaka 	sc->sc_msgver = sel_msg_ver;
23550517e57Snonaka 
23650517e57Snonaka 	/* One framework version. */
23750517e57Snonaka 	nego->ic_fwver_cnt = 1;
23850517e57Snonaka 	nego->ic_ver[0] = sel_fw_ver;
23950517e57Snonaka 
24050517e57Snonaka 	/* One message version. */
24150517e57Snonaka 	nego->ic_msgver_cnt = 1;
24250517e57Snonaka 	nego->ic_ver[1] = sel_msg_ver;
24350517e57Snonaka 
24450517e57Snonaka 	/* Update data size. */
24550517e57Snonaka 	nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ -
24650517e57Snonaka 	    sizeof(struct vmbus_icmsg_hdr);
24750517e57Snonaka 
24850517e57Snonaka 	/* Update total size, if necessary. */
24950517e57Snonaka 	if (dlen < VMBUS_IC_NEGOSZ)
25050517e57Snonaka 		*dlen0 = VMBUS_IC_NEGOSZ;
25150517e57Snonaka 
25250517e57Snonaka 	return 0;
25350517e57Snonaka }
25450517e57Snonaka 
25550517e57Snonaka int
vmbusic_sendresp(struct vmbusic_softc * sc,struct vmbus_channel * chan,void * data,uint32_t dlen,uint64_t rid)25650517e57Snonaka vmbusic_sendresp(struct vmbusic_softc *sc, struct vmbus_channel *chan,
25750517e57Snonaka     void *data, uint32_t dlen, uint64_t rid)
25850517e57Snonaka {
25950517e57Snonaka 	struct vmbus_icmsg_hdr *hdr;
26050517e57Snonaka 	int error;
26150517e57Snonaka 
26250517e57Snonaka 	KASSERTMSG(dlen >= sizeof(*hdr), "invalid data length %d", dlen);
26350517e57Snonaka 	hdr = data;
26450517e57Snonaka 
26550517e57Snonaka 	hdr->ic_flags = VMBUS_ICMSG_FLAG_TRANSACTION|VMBUS_ICMSG_FLAG_RESPONSE;
26650517e57Snonaka 	error = vmbus_channel_send(chan, data, dlen, rid,
26750517e57Snonaka 	    VMBUS_CHANPKT_TYPE_INBAND, 0);
26850517e57Snonaka 	if (error != 0)
26950517e57Snonaka 		device_printf(sc->sc_dev, "resp send failed: %d\n", error);
27050517e57Snonaka 	return error;
27150517e57Snonaka }
272