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