vmbus_ic.c revision 307845
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 307845 2016-10-24 05:36:19Z 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/sysctl.h> 41#include <sys/timetc.h> 42 43#include <dev/hyperv/include/hyperv.h> 44#include <dev/hyperv/include/vmbus.h> 45#include <dev/hyperv/utilities/hv_util.h> 46#include <dev/hyperv/utilities/vmbus_icreg.h> 47 48#include "vmbus_if.h" 49 50#define VMBUS_IC_BRSIZE (4 * PAGE_SIZE) 51 52#define VMBUS_IC_VERCNT 2 53#define VMBUS_IC_NEGOSZ \ 54 __offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT]) 55CTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE); 56 57static int vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS); 58static int vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS); 59 60int 61vmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen0, 62 uint32_t fw_ver, uint32_t msg_ver) 63{ 64 struct vmbus_icmsg_negotiate *nego; 65 int i, cnt, dlen = *dlen0, error; 66 uint32_t sel_fw_ver, sel_msg_ver; 67 bool has_fw_ver, has_msg_ver; 68 69 /* 70 * Preliminary message verification. 71 */ 72 if (dlen < sizeof(*nego)) { 73 device_printf(sc->ic_dev, "truncated ic negotiate, len %d\n", 74 dlen); 75 return (EINVAL); 76 } 77 nego = data; 78 79 if (nego->ic_fwver_cnt == 0) { 80 device_printf(sc->ic_dev, "ic negotiate does not contain " 81 "framework version %u\n", nego->ic_fwver_cnt); 82 return (EINVAL); 83 } 84 if (nego->ic_msgver_cnt == 0) { 85 device_printf(sc->ic_dev, "ic negotiate does not contain " 86 "message version %u\n", nego->ic_msgver_cnt); 87 return (EINVAL); 88 } 89 90 cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt; 91 if (dlen < __offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) { 92 device_printf(sc->ic_dev, "ic negotiate does not contain " 93 "versions %d\n", dlen); 94 return (EINVAL); 95 } 96 97 error = EOPNOTSUPP; 98 99 /* 100 * Find the best match framework version. 101 */ 102 has_fw_ver = false; 103 for (i = 0; i < nego->ic_fwver_cnt; ++i) { 104 if (VMBUS_ICVER_LE(nego->ic_ver[i], fw_ver)) { 105 if (!has_fw_ver) { 106 sel_fw_ver = nego->ic_ver[i]; 107 has_fw_ver = true; 108 } else if (VMBUS_ICVER_GT(nego->ic_ver[i], 109 sel_fw_ver)) { 110 sel_fw_ver = nego->ic_ver[i]; 111 } 112 } 113 } 114 if (!has_fw_ver) { 115 device_printf(sc->ic_dev, "failed to select framework " 116 "version\n"); 117 goto done; 118 } 119 120 /* 121 * Fine the best match message version. 122 */ 123 has_msg_ver = false; 124 for (i = nego->ic_fwver_cnt; 125 i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; ++i) { 126 if (VMBUS_ICVER_LE(nego->ic_ver[i], msg_ver)) { 127 if (!has_msg_ver) { 128 sel_msg_ver = nego->ic_ver[i]; 129 has_msg_ver = true; 130 } else if (VMBUS_ICVER_GT(nego->ic_ver[i], 131 sel_msg_ver)) { 132 sel_msg_ver = nego->ic_ver[i]; 133 } 134 } 135 } 136 if (!has_msg_ver) { 137 device_printf(sc->ic_dev, "failed to select message " 138 "version\n"); 139 goto done; 140 } 141 142 error = 0; 143done: 144 if (bootverbose || !has_fw_ver || !has_msg_ver) { 145 if (has_fw_ver) { 146 device_printf(sc->ic_dev, "sel framework version: " 147 "%u.%u\n", 148 VMBUS_ICVER_MAJOR(sel_fw_ver), 149 VMBUS_ICVER_MINOR(sel_fw_ver)); 150 } 151 for (i = 0; i < nego->ic_fwver_cnt; i++) { 152 device_printf(sc->ic_dev, "supp framework version: " 153 "%u.%u\n", 154 VMBUS_ICVER_MAJOR(nego->ic_ver[i]), 155 VMBUS_ICVER_MINOR(nego->ic_ver[i])); 156 } 157 158 if (has_msg_ver) { 159 device_printf(sc->ic_dev, "sel message version: " 160 "%u.%u\n", 161 VMBUS_ICVER_MAJOR(sel_msg_ver), 162 VMBUS_ICVER_MINOR(sel_msg_ver)); 163 } 164 for (i = nego->ic_fwver_cnt; 165 i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; i++) { 166 device_printf(sc->ic_dev, "supp message version: " 167 "%u.%u\n", 168 VMBUS_ICVER_MAJOR(nego->ic_ver[i]), 169 VMBUS_ICVER_MINOR(nego->ic_ver[i])); 170 } 171 } 172 if (error) 173 return (error); 174 175 /* Record the selected versions. */ 176 sc->ic_fwver = sel_fw_ver; 177 sc->ic_msgver = sel_msg_ver; 178 179 /* One framework version. */ 180 nego->ic_fwver_cnt = 1; 181 nego->ic_ver[0] = sel_fw_ver; 182 183 /* One message version. */ 184 nego->ic_msgver_cnt = 1; 185 nego->ic_ver[1] = sel_msg_ver; 186 187 /* Update data size. */ 188 nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ - 189 sizeof(struct vmbus_icmsg_hdr); 190 191 /* Update total size, if necessary. */ 192 if (dlen < VMBUS_IC_NEGOSZ) 193 *dlen0 = VMBUS_IC_NEGOSZ; 194 195 return (0); 196} 197 198int 199vmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[]) 200{ 201 device_t bus = device_get_parent(dev); 202 const struct vmbus_ic_desc *d; 203 204 if (resource_disabled(device_get_name(dev), 0)) 205 return (ENXIO); 206 207 for (d = descs; d->ic_desc != NULL; ++d) { 208 if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) { 209 device_set_desc(dev, d->ic_desc); 210 return (BUS_PROBE_DEFAULT); 211 } 212 } 213 return (ENXIO); 214} 215 216int 217hv_util_attach(device_t dev, vmbus_chan_callback_t cb) 218{ 219 struct hv_util_sc *sc = device_get_softc(dev); 220 struct vmbus_channel *chan = vmbus_get_channel(dev); 221 struct sysctl_oid_list *child; 222 struct sysctl_ctx_list *ctx; 223 int error; 224 225 sc->ic_dev = dev; 226 sc->ic_buflen = VMBUS_IC_BRSIZE; 227 sc->receive_buffer = malloc(VMBUS_IC_BRSIZE, M_DEVBUF, 228 M_WAITOK | M_ZERO); 229 230 /* 231 * These services are not performance critical and do not need 232 * batched reading. Furthermore, some services such as KVP can 233 * only handle one message from the host at a time. 234 * Turn off batched reading for all util drivers before we open the 235 * channel. 236 */ 237 vmbus_chan_set_readbatch(chan, false); 238 239 error = vmbus_chan_open(chan, VMBUS_IC_BRSIZE, VMBUS_IC_BRSIZE, NULL, 0, 240 cb, sc); 241 if (error) { 242 free(sc->receive_buffer, M_DEVBUF); 243 return (error); 244 } 245 246 ctx = device_get_sysctl_ctx(dev); 247 child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); 248 SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "fw_version", 249 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 250 vmbus_ic_fwver_sysctl, "A", "framework version"); 251 SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "msg_version", 252 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, 253 vmbus_ic_msgver_sysctl, "A", "message version"); 254 255 return (0); 256} 257 258static int 259vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS) 260{ 261 struct hv_util_sc *sc = arg1; 262 char verstr[16]; 263 264 snprintf(verstr, sizeof(verstr), "%u.%u", 265 VMBUS_ICVER_MAJOR(sc->ic_fwver), VMBUS_ICVER_MINOR(sc->ic_fwver)); 266 return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); 267} 268 269static int 270vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS) 271{ 272 struct hv_util_sc *sc = arg1; 273 char verstr[16]; 274 275 snprintf(verstr, sizeof(verstr), "%u.%u", 276 VMBUS_ICVER_MAJOR(sc->ic_msgver), VMBUS_ICVER_MINOR(sc->ic_msgver)); 277 return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); 278} 279 280int 281hv_util_detach(device_t dev) 282{ 283 struct hv_util_sc *sc = device_get_softc(dev); 284 285 vmbus_chan_close(vmbus_get_channel(dev)); 286 free(sc->receive_buffer, M_DEVBUF); 287 288 return (0); 289} 290