vmbus_ic.c revision 310312
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. 25271493Sdelphij * 26271493Sdelphij * $FreeBSD: head/sys/dev/hyperv/utilities/hv_util.c 310312 2016-12-20 04:51:14Z sephe $ 27250199Sgrehan */ 28250199Sgrehan 29271493Sdelphij/* 30250199Sgrehan * A common driver for all hyper-V util services. 31250199Sgrehan */ 32250199Sgrehan 33250199Sgrehan#include <sys/param.h> 34250199Sgrehan#include <sys/kernel.h> 35250199Sgrehan#include <sys/bus.h> 36250199Sgrehan#include <sys/malloc.h> 37250199Sgrehan#include <sys/module.h> 38250199Sgrehan#include <sys/reboot.h> 39304730Ssephe#include <sys/systm.h> 40307845Ssephe#include <sys/sysctl.h> 41250199Sgrehan#include <sys/timetc.h> 42250199Sgrehan 43250199Sgrehan#include <dev/hyperv/include/hyperv.h> 44303021Ssephe#include <dev/hyperv/include/vmbus.h> 45304273Ssephe#include <dev/hyperv/utilities/hv_util.h> 46304730Ssephe#include <dev/hyperv/utilities/vmbus_icreg.h> 47250199Sgrehan 48304273Ssephe#include "vmbus_if.h" 49304273Ssephe 50303823Ssephe#define VMBUS_IC_BRSIZE (4 * PAGE_SIZE) 51303823Ssephe 52304788Ssephe#define VMBUS_IC_VERCNT 2 53304788Ssephe#define VMBUS_IC_NEGOSZ \ 54304788Ssephe __offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT]) 55304788SsepheCTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE); 56304730Ssephe 57307845Ssephestatic int vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS); 58307845Ssephestatic int vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS); 59307845Ssephe 60304730Ssepheint 61307845Ssephevmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen0, 62307845Ssephe uint32_t fw_ver, uint32_t msg_ver) 63255414Sgrehan{ 64304730Ssephe struct vmbus_icmsg_negotiate *nego; 65307845Ssephe int i, cnt, dlen = *dlen0, error; 66307845Ssephe uint32_t sel_fw_ver, sel_msg_ver; 67307845Ssephe bool has_fw_ver, has_msg_ver; 68303822Ssephe 69304730Ssephe /* 70307845Ssephe * Preliminary message verification. 71304730Ssephe */ 72304730Ssephe if (dlen < sizeof(*nego)) { 73304730Ssephe device_printf(sc->ic_dev, "truncated ic negotiate, len %d\n", 74304730Ssephe dlen); 75307845Ssephe return (EINVAL); 76304730Ssephe } 77304730Ssephe nego = data; 78250199Sgrehan 79307845Ssephe if (nego->ic_fwver_cnt == 0) { 80307845Ssephe device_printf(sc->ic_dev, "ic negotiate does not contain " 81307845Ssephe "framework version %u\n", nego->ic_fwver_cnt); 82307845Ssephe return (EINVAL); 83307845Ssephe } 84307845Ssephe if (nego->ic_msgver_cnt == 0) { 85307845Ssephe device_printf(sc->ic_dev, "ic negotiate does not contain " 86307845Ssephe "message version %u\n", nego->ic_msgver_cnt); 87307845Ssephe return (EINVAL); 88307845Ssephe } 89307845Ssephe 90304730Ssephe cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt; 91304730Ssephe if (dlen < __offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) { 92304730Ssephe device_printf(sc->ic_dev, "ic negotiate does not contain " 93304730Ssephe "versions %d\n", dlen); 94307845Ssephe return (EINVAL); 95250199Sgrehan } 96250199Sgrehan 97307845Ssephe error = EOPNOTSUPP; 98304730Ssephe 99307845Ssephe /* 100307845Ssephe * Find the best match framework version. 101307845Ssephe */ 102307845Ssephe has_fw_ver = false; 103307845Ssephe for (i = 0; i < nego->ic_fwver_cnt; ++i) { 104307845Ssephe if (VMBUS_ICVER_LE(nego->ic_ver[i], fw_ver)) { 105307845Ssephe if (!has_fw_ver) { 106307845Ssephe sel_fw_ver = nego->ic_ver[i]; 107307845Ssephe has_fw_ver = true; 108307845Ssephe } else if (VMBUS_ICVER_GT(nego->ic_ver[i], 109307845Ssephe sel_fw_ver)) { 110307845Ssephe sel_fw_ver = nego->ic_ver[i]; 111307845Ssephe } 112307845Ssephe } 113307845Ssephe } 114307845Ssephe if (!has_fw_ver) { 115307845Ssephe device_printf(sc->ic_dev, "failed to select framework " 116307845Ssephe "version\n"); 117307845Ssephe goto done; 118307845Ssephe } 119307845Ssephe 120307845Ssephe /* 121307845Ssephe * Fine the best match message version. 122307845Ssephe */ 123307845Ssephe has_msg_ver = false; 124307845Ssephe for (i = nego->ic_fwver_cnt; 125307845Ssephe i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; ++i) { 126307845Ssephe if (VMBUS_ICVER_LE(nego->ic_ver[i], msg_ver)) { 127307845Ssephe if (!has_msg_ver) { 128307845Ssephe sel_msg_ver = nego->ic_ver[i]; 129307845Ssephe has_msg_ver = true; 130307845Ssephe } else if (VMBUS_ICVER_GT(nego->ic_ver[i], 131307845Ssephe sel_msg_ver)) { 132307845Ssephe sel_msg_ver = nego->ic_ver[i]; 133307845Ssephe } 134307845Ssephe } 135307845Ssephe } 136307845Ssephe if (!has_msg_ver) { 137307845Ssephe device_printf(sc->ic_dev, "failed to select message " 138307845Ssephe "version\n"); 139307845Ssephe goto done; 140307845Ssephe } 141307845Ssephe 142307845Ssephe error = 0; 143307845Ssephedone: 144307845Ssephe if (bootverbose || !has_fw_ver || !has_msg_ver) { 145307845Ssephe if (has_fw_ver) { 146307845Ssephe device_printf(sc->ic_dev, "sel framework version: " 147307845Ssephe "%u.%u\n", 148307845Ssephe VMBUS_ICVER_MAJOR(sel_fw_ver), 149307845Ssephe VMBUS_ICVER_MINOR(sel_fw_ver)); 150307845Ssephe } 151307845Ssephe for (i = 0; i < nego->ic_fwver_cnt; i++) { 152307845Ssephe device_printf(sc->ic_dev, "supp framework version: " 153307845Ssephe "%u.%u\n", 154307845Ssephe VMBUS_ICVER_MAJOR(nego->ic_ver[i]), 155307845Ssephe VMBUS_ICVER_MINOR(nego->ic_ver[i])); 156307845Ssephe } 157307845Ssephe 158307845Ssephe if (has_msg_ver) { 159307845Ssephe device_printf(sc->ic_dev, "sel message version: " 160307845Ssephe "%u.%u\n", 161307845Ssephe VMBUS_ICVER_MAJOR(sel_msg_ver), 162307845Ssephe VMBUS_ICVER_MINOR(sel_msg_ver)); 163307845Ssephe } 164307845Ssephe for (i = nego->ic_fwver_cnt; 165307845Ssephe i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; i++) { 166307845Ssephe device_printf(sc->ic_dev, "supp message version: " 167307845Ssephe "%u.%u\n", 168307845Ssephe VMBUS_ICVER_MAJOR(nego->ic_ver[i]), 169307845Ssephe VMBUS_ICVER_MINOR(nego->ic_ver[i])); 170307845Ssephe } 171307845Ssephe } 172307845Ssephe if (error) 173307845Ssephe return (error); 174307845Ssephe 175307845Ssephe /* Record the selected versions. */ 176307845Ssephe sc->ic_fwver = sel_fw_ver; 177307845Ssephe sc->ic_msgver = sel_msg_ver; 178307845Ssephe 179307845Ssephe /* One framework version. */ 180304730Ssephe nego->ic_fwver_cnt = 1; 181307845Ssephe nego->ic_ver[0] = sel_fw_ver; 182304730Ssephe 183307845Ssephe /* One message version. */ 184304730Ssephe nego->ic_msgver_cnt = 1; 185307845Ssephe nego->ic_ver[1] = sel_msg_ver; 186304730Ssephe 187307845Ssephe /* Update data size. */ 188304788Ssephe nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ - 189304788Ssephe sizeof(struct vmbus_icmsg_hdr); 190304730Ssephe 191307845Ssephe /* Update total size, if necessary. */ 192304788Ssephe if (dlen < VMBUS_IC_NEGOSZ) 193304788Ssephe *dlen0 = VMBUS_IC_NEGOSZ; 194304788Ssephe 195307845Ssephe return (0); 196250199Sgrehan} 197250199Sgrehan 198295958Ssepheint 199304273Ssephevmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[]) 200304273Ssephe{ 201304273Ssephe device_t bus = device_get_parent(dev); 202304273Ssephe const struct vmbus_ic_desc *d; 203304273Ssephe 204304273Ssephe if (resource_disabled(device_get_name(dev), 0)) 205304273Ssephe return (ENXIO); 206304273Ssephe 207304273Ssephe for (d = descs; d->ic_desc != NULL; ++d) { 208304273Ssephe if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) { 209304273Ssephe device_set_desc(dev, d->ic_desc); 210304273Ssephe return (BUS_PROBE_DEFAULT); 211304273Ssephe } 212304273Ssephe } 213304273Ssephe return (ENXIO); 214304273Ssephe} 215304273Ssephe 216304273Ssepheint 217303824Ssephehv_util_attach(device_t dev, vmbus_chan_callback_t cb) 218250199Sgrehan{ 219303823Ssephe struct hv_util_sc *sc = device_get_softc(dev); 220303823Ssephe struct vmbus_channel *chan = vmbus_get_channel(dev); 221307845Ssephe struct sysctl_oid_list *child; 222307845Ssephe struct sysctl_ctx_list *ctx; 223303823Ssephe int error; 224250199Sgrehan 225304730Ssephe sc->ic_dev = dev; 226303823Ssephe sc->ic_buflen = VMBUS_IC_BRSIZE; 227303823Ssephe sc->receive_buffer = malloc(VMBUS_IC_BRSIZE, M_DEVBUF, 228303823Ssephe M_WAITOK | M_ZERO); 229250199Sgrehan 230282212Swhu /* 231282212Swhu * These services are not performance critical and do not need 232282212Swhu * batched reading. Furthermore, some services such as KVP can 233282212Swhu * only handle one message from the host at a time. 234282212Swhu * Turn off batched reading for all util drivers before we open the 235282212Swhu * channel. 236282212Swhu */ 237303069Ssephe vmbus_chan_set_readbatch(chan, false); 238282212Swhu 239303823Ssephe error = vmbus_chan_open(chan, VMBUS_IC_BRSIZE, VMBUS_IC_BRSIZE, NULL, 0, 240303824Ssephe cb, sc); 241303823Ssephe if (error) { 242303823Ssephe free(sc->receive_buffer, M_DEVBUF); 243303823Ssephe return (error); 244303823Ssephe } 245307845Ssephe 246307845Ssephe ctx = device_get_sysctl_ctx(dev); 247307845Ssephe child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); 248307845Ssephe SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "fw_version", 249307845Ssephe CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 250307845Ssephe vmbus_ic_fwver_sysctl, "A", "framework version"); 251307845Ssephe SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "msg_version", 252307845Ssephe CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 253307845Ssephe vmbus_ic_msgver_sysctl, "A", "message version"); 254307845Ssephe 255250199Sgrehan return (0); 256250199Sgrehan} 257250199Sgrehan 258307845Ssephestatic int 259307845Ssephevmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS) 260307845Ssephe{ 261307845Ssephe struct hv_util_sc *sc = arg1; 262307845Ssephe char verstr[16]; 263307845Ssephe 264307845Ssephe snprintf(verstr, sizeof(verstr), "%u.%u", 265307845Ssephe VMBUS_ICVER_MAJOR(sc->ic_fwver), VMBUS_ICVER_MINOR(sc->ic_fwver)); 266307845Ssephe return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); 267307845Ssephe} 268307845Ssephe 269307845Ssephestatic int 270307845Ssephevmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS) 271307845Ssephe{ 272307845Ssephe struct hv_util_sc *sc = arg1; 273307845Ssephe char verstr[16]; 274307845Ssephe 275307845Ssephe snprintf(verstr, sizeof(verstr), "%u.%u", 276307845Ssephe VMBUS_ICVER_MAJOR(sc->ic_msgver), VMBUS_ICVER_MINOR(sc->ic_msgver)); 277307845Ssephe return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); 278307845Ssephe} 279307845Ssephe 280295958Ssepheint 281250199Sgrehanhv_util_detach(device_t dev) 282250199Sgrehan{ 283302702Ssephe struct hv_util_sc *sc = device_get_softc(dev); 284250199Sgrehan 285303069Ssephe vmbus_chan_close(vmbus_get_channel(dev)); 286302702Ssephe free(sc->receive_buffer, M_DEVBUF); 287250199Sgrehan 288250199Sgrehan return (0); 289250199Sgrehan} 290310312Ssephe 291310312Ssepheint 292310312Ssephevmbus_ic_sendresp(struct hv_util_sc *sc, struct vmbus_channel *chan, 293310312Ssephe void *data, int dlen, uint64_t xactid) 294310312Ssephe{ 295310312Ssephe struct vmbus_icmsg_hdr *hdr; 296310312Ssephe int error; 297310312Ssephe 298310312Ssephe KASSERT(dlen >= sizeof(*hdr), ("invalid data length %d", dlen)); 299310312Ssephe hdr = data; 300310312Ssephe 301310312Ssephe hdr->ic_flags = VMBUS_ICMSG_FLAG_XACT | VMBUS_ICMSG_FLAG_RESP; 302310312Ssephe error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_INBAND, 0, 303310312Ssephe data, dlen, xactid); 304310312Ssephe if (error) 305310312Ssephe device_printf(sc->ic_dev, "resp send failed: %d\n", error); 306310312Ssephe return (error); 307310312Ssephe} 308