1250199Sgrehan/*- 2298446Ssephe * Copyright (c) 2014,2016 Microsoft Corp. 3250199Sgrehan * All rights reserved. 4250199Sgrehan * 5250199Sgrehan * Redistribution and use in source and binary forms, with or without 6250199Sgrehan * modification, are permitted provided that the following conditions 7250199Sgrehan * are met: 8250199Sgrehan * 1. Redistributions of source code must retain the above copyright 9250199Sgrehan * notice unmodified, this list of conditions, and the following 10250199Sgrehan * disclaimer. 11250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 12250199Sgrehan * notice, this list of conditions and the following disclaimer in the 13250199Sgrehan * documentation and/or other materials provided with the distribution. 14250199Sgrehan * 15250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25250199Sgrehan */ 26250199Sgrehan 27310315Ssephe#include <sys/cdefs.h> 28310315Ssephe__FBSDID("$FreeBSD: stable/11/sys/dev/hyperv/utilities/vmbus_ic.c 311383 2017-01-05 08:21:46Z sephe $"); 29250199Sgrehan 30250199Sgrehan#include <sys/param.h> 31250199Sgrehan#include <sys/bus.h> 32250199Sgrehan#include <sys/malloc.h> 33304730Ssephe#include <sys/systm.h> 34307845Ssephe#include <sys/sysctl.h> 35250199Sgrehan 36250199Sgrehan#include <dev/hyperv/include/hyperv.h> 37303021Ssephe#include <dev/hyperv/include/vmbus.h> 38304730Ssephe#include <dev/hyperv/utilities/vmbus_icreg.h> 39310314Ssephe#include <dev/hyperv/utilities/vmbus_icvar.h> 40250199Sgrehan 41304273Ssephe#include "vmbus_if.h" 42304273Ssephe 43303823Ssephe#define VMBUS_IC_BRSIZE (4 * PAGE_SIZE) 44303823Ssephe 45304788Ssephe#define VMBUS_IC_VERCNT 2 46304788Ssephe#define VMBUS_IC_NEGOSZ \ 47304788Ssephe __offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT]) 48304788SsepheCTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE); 49304730Ssephe 50307845Ssephestatic int vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS); 51307845Ssephestatic int vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS); 52307845Ssephe 53304730Ssepheint 54310313Ssephevmbus_ic_negomsg(struct vmbus_ic_softc *sc, void *data, int *dlen0, 55307845Ssephe uint32_t fw_ver, uint32_t msg_ver) 56255414Sgrehan{ 57304730Ssephe struct vmbus_icmsg_negotiate *nego; 58307845Ssephe int i, cnt, dlen = *dlen0, error; 59307845Ssephe uint32_t sel_fw_ver, sel_msg_ver; 60307845Ssephe bool has_fw_ver, has_msg_ver; 61303822Ssephe 62304730Ssephe /* 63307845Ssephe * Preliminary message verification. 64304730Ssephe */ 65304730Ssephe if (dlen < sizeof(*nego)) { 66304730Ssephe device_printf(sc->ic_dev, "truncated ic negotiate, len %d\n", 67304730Ssephe dlen); 68307845Ssephe return (EINVAL); 69304730Ssephe } 70304730Ssephe nego = data; 71250199Sgrehan 72307845Ssephe if (nego->ic_fwver_cnt == 0) { 73307845Ssephe device_printf(sc->ic_dev, "ic negotiate does not contain " 74307845Ssephe "framework version %u\n", nego->ic_fwver_cnt); 75307845Ssephe return (EINVAL); 76307845Ssephe } 77307845Ssephe if (nego->ic_msgver_cnt == 0) { 78307845Ssephe device_printf(sc->ic_dev, "ic negotiate does not contain " 79307845Ssephe "message version %u\n", nego->ic_msgver_cnt); 80307845Ssephe return (EINVAL); 81307845Ssephe } 82307845Ssephe 83304730Ssephe cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt; 84304730Ssephe if (dlen < __offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) { 85304730Ssephe device_printf(sc->ic_dev, "ic negotiate does not contain " 86304730Ssephe "versions %d\n", dlen); 87307845Ssephe return (EINVAL); 88250199Sgrehan } 89250199Sgrehan 90307845Ssephe error = EOPNOTSUPP; 91304730Ssephe 92307845Ssephe /* 93307845Ssephe * Find the best match framework version. 94307845Ssephe */ 95307845Ssephe has_fw_ver = false; 96307845Ssephe for (i = 0; i < nego->ic_fwver_cnt; ++i) { 97307845Ssephe if (VMBUS_ICVER_LE(nego->ic_ver[i], fw_ver)) { 98307845Ssephe if (!has_fw_ver) { 99307845Ssephe sel_fw_ver = nego->ic_ver[i]; 100307845Ssephe has_fw_ver = true; 101307845Ssephe } else if (VMBUS_ICVER_GT(nego->ic_ver[i], 102307845Ssephe sel_fw_ver)) { 103307845Ssephe sel_fw_ver = nego->ic_ver[i]; 104307845Ssephe } 105307845Ssephe } 106307845Ssephe } 107307845Ssephe if (!has_fw_ver) { 108307845Ssephe device_printf(sc->ic_dev, "failed to select framework " 109307845Ssephe "version\n"); 110307845Ssephe goto done; 111307845Ssephe } 112307845Ssephe 113307845Ssephe /* 114307845Ssephe * Fine the best match message version. 115307845Ssephe */ 116307845Ssephe has_msg_ver = false; 117307845Ssephe for (i = nego->ic_fwver_cnt; 118307845Ssephe i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; ++i) { 119307845Ssephe if (VMBUS_ICVER_LE(nego->ic_ver[i], msg_ver)) { 120307845Ssephe if (!has_msg_ver) { 121307845Ssephe sel_msg_ver = nego->ic_ver[i]; 122307845Ssephe has_msg_ver = true; 123307845Ssephe } else if (VMBUS_ICVER_GT(nego->ic_ver[i], 124307845Ssephe sel_msg_ver)) { 125307845Ssephe sel_msg_ver = nego->ic_ver[i]; 126307845Ssephe } 127307845Ssephe } 128307845Ssephe } 129307845Ssephe if (!has_msg_ver) { 130307845Ssephe device_printf(sc->ic_dev, "failed to select message " 131307845Ssephe "version\n"); 132307845Ssephe goto done; 133307845Ssephe } 134307845Ssephe 135307845Ssephe error = 0; 136307845Ssephedone: 137307845Ssephe if (bootverbose || !has_fw_ver || !has_msg_ver) { 138307845Ssephe if (has_fw_ver) { 139307845Ssephe device_printf(sc->ic_dev, "sel framework version: " 140307845Ssephe "%u.%u\n", 141307845Ssephe VMBUS_ICVER_MAJOR(sel_fw_ver), 142307845Ssephe VMBUS_ICVER_MINOR(sel_fw_ver)); 143307845Ssephe } 144307845Ssephe for (i = 0; i < nego->ic_fwver_cnt; i++) { 145307845Ssephe device_printf(sc->ic_dev, "supp framework version: " 146307845Ssephe "%u.%u\n", 147307845Ssephe VMBUS_ICVER_MAJOR(nego->ic_ver[i]), 148307845Ssephe VMBUS_ICVER_MINOR(nego->ic_ver[i])); 149307845Ssephe } 150307845Ssephe 151307845Ssephe if (has_msg_ver) { 152307845Ssephe device_printf(sc->ic_dev, "sel message version: " 153307845Ssephe "%u.%u\n", 154307845Ssephe VMBUS_ICVER_MAJOR(sel_msg_ver), 155307845Ssephe VMBUS_ICVER_MINOR(sel_msg_ver)); 156307845Ssephe } 157307845Ssephe for (i = nego->ic_fwver_cnt; 158307845Ssephe i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; i++) { 159307845Ssephe device_printf(sc->ic_dev, "supp message version: " 160307845Ssephe "%u.%u\n", 161307845Ssephe VMBUS_ICVER_MAJOR(nego->ic_ver[i]), 162307845Ssephe VMBUS_ICVER_MINOR(nego->ic_ver[i])); 163307845Ssephe } 164307845Ssephe } 165307845Ssephe if (error) 166307845Ssephe return (error); 167307845Ssephe 168307845Ssephe /* Record the selected versions. */ 169307845Ssephe sc->ic_fwver = sel_fw_ver; 170307845Ssephe sc->ic_msgver = sel_msg_ver; 171307845Ssephe 172307845Ssephe /* One framework version. */ 173304730Ssephe nego->ic_fwver_cnt = 1; 174307845Ssephe nego->ic_ver[0] = sel_fw_ver; 175304730Ssephe 176307845Ssephe /* One message version. */ 177304730Ssephe nego->ic_msgver_cnt = 1; 178307845Ssephe nego->ic_ver[1] = sel_msg_ver; 179304730Ssephe 180307845Ssephe /* Update data size. */ 181304788Ssephe nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ - 182304788Ssephe sizeof(struct vmbus_icmsg_hdr); 183304730Ssephe 184307845Ssephe /* Update total size, if necessary. */ 185304788Ssephe if (dlen < VMBUS_IC_NEGOSZ) 186304788Ssephe *dlen0 = VMBUS_IC_NEGOSZ; 187304788Ssephe 188307845Ssephe return (0); 189250199Sgrehan} 190250199Sgrehan 191295958Ssepheint 192304273Ssephevmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[]) 193304273Ssephe{ 194304273Ssephe device_t bus = device_get_parent(dev); 195304273Ssephe const struct vmbus_ic_desc *d; 196304273Ssephe 197304273Ssephe if (resource_disabled(device_get_name(dev), 0)) 198304273Ssephe return (ENXIO); 199304273Ssephe 200304273Ssephe for (d = descs; d->ic_desc != NULL; ++d) { 201304273Ssephe if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) { 202304273Ssephe device_set_desc(dev, d->ic_desc); 203304273Ssephe return (BUS_PROBE_DEFAULT); 204304273Ssephe } 205304273Ssephe } 206304273Ssephe return (ENXIO); 207304273Ssephe} 208304273Ssephe 209304273Ssepheint 210310313Ssephevmbus_ic_attach(device_t dev, vmbus_chan_callback_t cb) 211250199Sgrehan{ 212310313Ssephe struct vmbus_ic_softc *sc = device_get_softc(dev); 213303823Ssephe struct vmbus_channel *chan = vmbus_get_channel(dev); 214307845Ssephe struct sysctl_oid_list *child; 215307845Ssephe struct sysctl_ctx_list *ctx; 216303823Ssephe int error; 217250199Sgrehan 218304730Ssephe sc->ic_dev = dev; 219303823Ssephe sc->ic_buflen = VMBUS_IC_BRSIZE; 220310313Ssephe sc->ic_buf = malloc(VMBUS_IC_BRSIZE, M_DEVBUF, M_WAITOK | M_ZERO); 221250199Sgrehan 222282212Swhu /* 223282212Swhu * These services are not performance critical and do not need 224282212Swhu * batched reading. Furthermore, some services such as KVP can 225282212Swhu * only handle one message from the host at a time. 226282212Swhu * Turn off batched reading for all util drivers before we open the 227282212Swhu * channel. 228282212Swhu */ 229303069Ssephe vmbus_chan_set_readbatch(chan, false); 230282212Swhu 231303823Ssephe error = vmbus_chan_open(chan, VMBUS_IC_BRSIZE, VMBUS_IC_BRSIZE, NULL, 0, 232303824Ssephe cb, sc); 233303823Ssephe if (error) { 234310313Ssephe free(sc->ic_buf, M_DEVBUF); 235303823Ssephe return (error); 236303823Ssephe } 237307845Ssephe 238307845Ssephe ctx = device_get_sysctl_ctx(dev); 239307845Ssephe child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); 240307845Ssephe SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "fw_version", 241307845Ssephe CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 242307845Ssephe vmbus_ic_fwver_sysctl, "A", "framework version"); 243307845Ssephe SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "msg_version", 244307845Ssephe CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 245307845Ssephe vmbus_ic_msgver_sysctl, "A", "message version"); 246307845Ssephe 247250199Sgrehan return (0); 248250199Sgrehan} 249250199Sgrehan 250307845Ssephestatic int 251307845Ssephevmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS) 252307845Ssephe{ 253310313Ssephe struct vmbus_ic_softc *sc = arg1; 254307845Ssephe char verstr[16]; 255307845Ssephe 256307845Ssephe snprintf(verstr, sizeof(verstr), "%u.%u", 257307845Ssephe VMBUS_ICVER_MAJOR(sc->ic_fwver), VMBUS_ICVER_MINOR(sc->ic_fwver)); 258307845Ssephe return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); 259307845Ssephe} 260307845Ssephe 261307845Ssephestatic int 262307845Ssephevmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS) 263307845Ssephe{ 264310313Ssephe struct vmbus_ic_softc *sc = arg1; 265307845Ssephe char verstr[16]; 266307845Ssephe 267307845Ssephe snprintf(verstr, sizeof(verstr), "%u.%u", 268307845Ssephe VMBUS_ICVER_MAJOR(sc->ic_msgver), VMBUS_ICVER_MINOR(sc->ic_msgver)); 269307845Ssephe return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); 270307845Ssephe} 271307845Ssephe 272295958Ssepheint 273310313Ssephevmbus_ic_detach(device_t dev) 274250199Sgrehan{ 275310313Ssephe struct vmbus_ic_softc *sc = device_get_softc(dev); 276250199Sgrehan 277303069Ssephe vmbus_chan_close(vmbus_get_channel(dev)); 278310313Ssephe free(sc->ic_buf, M_DEVBUF); 279250199Sgrehan 280250199Sgrehan return (0); 281250199Sgrehan} 282310312Ssephe 283310312Ssepheint 284310313Ssephevmbus_ic_sendresp(struct vmbus_ic_softc *sc, struct vmbus_channel *chan, 285310312Ssephe void *data, int dlen, uint64_t xactid) 286310312Ssephe{ 287310312Ssephe struct vmbus_icmsg_hdr *hdr; 288310312Ssephe int error; 289310312Ssephe 290310312Ssephe KASSERT(dlen >= sizeof(*hdr), ("invalid data length %d", dlen)); 291310312Ssephe hdr = data; 292310312Ssephe 293310312Ssephe hdr->ic_flags = VMBUS_ICMSG_FLAG_XACT | VMBUS_ICMSG_FLAG_RESP; 294310312Ssephe error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_INBAND, 0, 295310312Ssephe data, dlen, xactid); 296310312Ssephe if (error) 297310312Ssephe device_printf(sc->ic_dev, "resp send failed: %d\n", error); 298310312Ssephe return (error); 299310312Ssephe} 300