summaryrefslogtreecommitdiffstats
path: root/sys/dev/hyperv/utilities/hv_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/hyperv/utilities/hv_util.c')
-rw-r--r--sys/dev/hyperv/utilities/hv_util.c162
1 files changed, 145 insertions, 17 deletions
diff --git a/sys/dev/hyperv/utilities/hv_util.c b/sys/dev/hyperv/utilities/hv_util.c
index 3fc16c9..20b8f84 100644
--- a/sys/dev/hyperv/utilities/hv_util.c
+++ b/sys/dev/hyperv/utilities/hv_util.c
@@ -37,6 +37,7 @@
#include <sys/module.h>
#include <sys/reboot.h>
#include <sys/systm.h>
+#include <sys/sysctl.h>
#include <sys/timetc.h>
#include <dev/hyperv/include/hyperv.h>
@@ -53,52 +54,145 @@
__offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT])
CTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE);
+static int vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS);
+static int vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS);
+
int
-vmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen0)
+vmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen0,
+ uint32_t fw_ver, uint32_t msg_ver)
{
struct vmbus_icmsg_negotiate *nego;
- int cnt, major, dlen = *dlen0;
+ int i, cnt, dlen = *dlen0, error;
+ uint32_t sel_fw_ver, sel_msg_ver;
+ bool has_fw_ver, has_msg_ver;
/*
- * Preliminary message size verification
+ * Preliminary message verification.
*/
if (dlen < sizeof(*nego)) {
device_printf(sc->ic_dev, "truncated ic negotiate, len %d\n",
dlen);
- return EINVAL;
+ return (EINVAL);
}
nego = data;
+ if (nego->ic_fwver_cnt == 0) {
+ device_printf(sc->ic_dev, "ic negotiate does not contain "
+ "framework version %u\n", nego->ic_fwver_cnt);
+ return (EINVAL);
+ }
+ if (nego->ic_msgver_cnt == 0) {
+ device_printf(sc->ic_dev, "ic negotiate does not contain "
+ "message version %u\n", nego->ic_msgver_cnt);
+ return (EINVAL);
+ }
+
cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt;
if (dlen < __offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) {
device_printf(sc->ic_dev, "ic negotiate does not contain "
"versions %d\n", dlen);
- return EINVAL;
+ return (EINVAL);
+ }
+
+ error = EOPNOTSUPP;
+
+ /*
+ * Find the best match framework version.
+ */
+ has_fw_ver = false;
+ for (i = 0; i < nego->ic_fwver_cnt; ++i) {
+ if (VMBUS_ICVER_LE(nego->ic_ver[i], fw_ver)) {
+ if (!has_fw_ver) {
+ sel_fw_ver = nego->ic_ver[i];
+ has_fw_ver = true;
+ } else if (VMBUS_ICVER_GT(nego->ic_ver[i],
+ sel_fw_ver)) {
+ sel_fw_ver = nego->ic_ver[i];
+ }
+ }
+ }
+ if (!has_fw_ver) {
+ device_printf(sc->ic_dev, "failed to select framework "
+ "version\n");
+ goto done;
+ }
+
+ /*
+ * Fine the best match message version.
+ */
+ has_msg_ver = false;
+ for (i = nego->ic_fwver_cnt;
+ i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; ++i) {
+ if (VMBUS_ICVER_LE(nego->ic_ver[i], msg_ver)) {
+ if (!has_msg_ver) {
+ sel_msg_ver = nego->ic_ver[i];
+ has_msg_ver = true;
+ } else if (VMBUS_ICVER_GT(nego->ic_ver[i],
+ sel_msg_ver)) {
+ sel_msg_ver = nego->ic_ver[i];
+ }
+ }
+ }
+ if (!has_msg_ver) {
+ device_printf(sc->ic_dev, "failed to select message "
+ "version\n");
+ goto done;
}
- /* Select major version; XXX looks wrong. */
- if (nego->ic_fwver_cnt >= 2 && VMBUS_ICVER_MAJOR(nego->ic_ver[1]) == 3)
- major = 3;
- else
- major = 1;
+ error = 0;
+done:
+ if (bootverbose || !has_fw_ver || !has_msg_ver) {
+ if (has_fw_ver) {
+ device_printf(sc->ic_dev, "sel framework version: "
+ "%u.%u\n",
+ VMBUS_ICVER_MAJOR(sel_fw_ver),
+ VMBUS_ICVER_MINOR(sel_fw_ver));
+ }
+ for (i = 0; i < nego->ic_fwver_cnt; i++) {
+ device_printf(sc->ic_dev, "supp framework version: "
+ "%u.%u\n",
+ VMBUS_ICVER_MAJOR(nego->ic_ver[i]),
+ VMBUS_ICVER_MINOR(nego->ic_ver[i]));
+ }
+
+ if (has_msg_ver) {
+ device_printf(sc->ic_dev, "sel message version: "
+ "%u.%u\n",
+ VMBUS_ICVER_MAJOR(sel_msg_ver),
+ VMBUS_ICVER_MINOR(sel_msg_ver));
+ }
+ for (i = nego->ic_fwver_cnt;
+ i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; i++) {
+ device_printf(sc->ic_dev, "supp message version: "
+ "%u.%u\n",
+ VMBUS_ICVER_MAJOR(nego->ic_ver[i]),
+ VMBUS_ICVER_MINOR(nego->ic_ver[i]));
+ }
+ }
+ if (error)
+ return (error);
+
+ /* Record the selected versions. */
+ sc->ic_fwver = sel_fw_ver;
+ sc->ic_msgver = sel_msg_ver;
- /* One framework version */
+ /* One framework version. */
nego->ic_fwver_cnt = 1;
- nego->ic_ver[0] = VMBUS_IC_VERSION(major, 0);
+ nego->ic_ver[0] = sel_fw_ver;
- /* One message version */
+ /* One message version. */
nego->ic_msgver_cnt = 1;
- nego->ic_ver[1] = VMBUS_IC_VERSION(major, 0);
+ nego->ic_ver[1] = sel_msg_ver;
- /* Update data size */
+ /* Update data size. */
nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ -
sizeof(struct vmbus_icmsg_hdr);
- /* Update total size, if necessary */
+ /* Update total size, if necessary. */
if (dlen < VMBUS_IC_NEGOSZ)
*dlen0 = VMBUS_IC_NEGOSZ;
- return 0;
+ return (0);
}
int
@@ -124,6 +218,8 @@ hv_util_attach(device_t dev, vmbus_chan_callback_t cb)
{
struct hv_util_sc *sc = device_get_softc(dev);
struct vmbus_channel *chan = vmbus_get_channel(dev);
+ struct sysctl_oid_list *child;
+ struct sysctl_ctx_list *ctx;
int error;
sc->ic_dev = dev;
@@ -146,9 +242,41 @@ hv_util_attach(device_t dev, vmbus_chan_callback_t cb)
free(sc->receive_buffer, M_DEVBUF);
return (error);
}
+
+ ctx = device_get_sysctl_ctx(dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "fw_version",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
+ vmbus_ic_fwver_sysctl, "A", "framework version");
+ SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "msg_version",
+ CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
+ vmbus_ic_msgver_sysctl, "A", "message version");
+
return (0);
}
+static int
+vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct hv_util_sc *sc = arg1;
+ char verstr[16];
+
+ snprintf(verstr, sizeof(verstr), "%u.%u",
+ VMBUS_ICVER_MAJOR(sc->ic_fwver), VMBUS_ICVER_MINOR(sc->ic_fwver));
+ return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
+}
+
+static int
+vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ struct hv_util_sc *sc = arg1;
+ char verstr[16];
+
+ snprintf(verstr, sizeof(verstr), "%u.%u",
+ VMBUS_ICVER_MAJOR(sc->ic_msgver), VMBUS_ICVER_MINOR(sc->ic_msgver));
+ return sysctl_handle_string(oidp, verstr, sizeof(verstr), req);
+}
+
int
hv_util_detach(device_t dev)
{
OpenPOWER on IntegriCloud