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