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