summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2016-01-27 09:04:20 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2016-01-27 09:04:20 +0000
commit24f69262881c97ea25f1d4b8bb9616475a37900b (patch)
tree98fb61e459b195abc5491f25ba62d44ef834c990 /sys/dev
parentd1e5520d3416e582a49f9a4c3bd7d63b5da7f401 (diff)
Add a key-value interface to pvbus(4) that allows to get or set values
in the underlying information store of the host from the OpenBSD-VM's userspace. OpenBSD did not provide access to these stores before, mostly because we did not want to add a custom tool and interface for each hypervisor. The pvbus(4) interface provides backends for xen(4)'s XenStore and vmt(4)'s VMware Tools "guestinfo". These information stores are fairly different, XenStore is a "filesystem" while vmt is a RPC, and the key-value abstraction limits them a bit but provides the most wanted functionality. Discussed with many OK mikeb@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pv/pvbus.c147
-rw-r--r--sys/dev/pv/pvvar.h24
-rw-r--r--sys/dev/pv/vmt.c59
-rw-r--r--sys/dev/pv/xen.c6
-rw-r--r--sys/dev/pv/xenstore.c70
-rw-r--r--sys/dev/pv/xenvar.h3
6 files changed, 302 insertions, 7 deletions
diff --git a/sys/dev/pv/pvbus.c b/sys/dev/pv/pvbus.c
index 91c7c622140..49bbcb33d92 100644
--- a/sys/dev/pv/pvbus.c
+++ b/sys/dev/pv/pvbus.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pvbus.c,v 1.10 2015/12/12 12:47:49 reyk Exp $ */
+/* $OpenBSD: pvbus.c,v 1.11 2016/01/27 09:04:19 reyk Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -29,9 +29,12 @@
#include <sys/syslog.h>
#include <sys/proc.h>
#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
#include <machine/specialreg.h>
#include <machine/cpu.h>
+#include <machine/conf.h>
#ifdef __amd64__
#include <machine/vmmvar.h>
#endif
@@ -59,6 +62,9 @@ void pvbus_hyperv_print(struct pvbus_hv *);
void pvbus_xen(struct pvbus_hv *);
void pvbus_xen_print(struct pvbus_hv *);
+int pvbus_minor(struct pvbus_softc *, dev_t);
+int pvbusgetstr(size_t, const char *, char **);
+
struct cfattach pvbus_ca = {
sizeof(struct pvbus_softc),
pvbus_match,
@@ -90,6 +96,7 @@ struct pvbus_type {
};
struct pvbus_hv pvbus_hv[PVBUS_MAX];
+struct pvbus_softc *pvbus_softc;
int
pvbus_probe(void)
@@ -114,6 +121,7 @@ pvbus_attach(struct device *parent, struct device *self, void *aux)
int i, cnt;
sc->pvbus_hv = pvbus_hv;
+ pvbus_softc = sc;
printf(":");
for (i = 0, cnt = 0; i < PVBUS_MAX; i++) {
@@ -276,3 +284,140 @@ pvbus_xen_print(struct pvbus_hv *hv)
{
printf(" %u.%u", hv->hv_major, hv->hv_minor);
}
+
+int
+pvbus_minor(struct pvbus_softc *sc, dev_t dev)
+{
+ int hvid, cnt;
+ struct pvbus_hv *hv;
+
+ for (hvid = 0, cnt = 0; hvid < PVBUS_MAX; hvid++) {
+ hv = &sc->pvbus_hv[hvid];
+ if (hv->hv_base == 0 || hv->hv_kvop == NULL)
+ continue;
+ if (minor(dev) == cnt++)
+ return (hvid);
+ }
+
+ return (-1);
+}
+
+int
+pvbusopen(dev_t dev, int flags, int mode, struct proc *p)
+{
+ if (pvbus_softc == NULL)
+ return (ENODEV);
+ if (pvbus_minor(pvbus_softc, dev) == -1)
+ return (ENXIO);
+ return (0);
+}
+
+int
+pvbusclose(dev_t dev, int flags, int mode, struct proc *p)
+{
+ if (pvbus_softc == NULL)
+ return (ENODEV);
+ if (pvbus_minor(pvbus_softc, dev) == -1)
+ return (ENXIO);
+ return (0);
+}
+
+int
+pvbusgetstr(size_t srclen, const char *src, char **dstp)
+{
+ int error = 0;
+ char *dst;
+
+ /*
+ * Reject size that is too short or obviously too long:
+ * - at least one byte for the nul terminator.
+ * - PAGE_SIZE is an arbitrary value, but known pv backends seem
+ * to have a hard (PAGE_SIZE - x) limit in their messaging.
+ */
+ if (srclen < 1)
+ return (EINVAL);
+ else if (srclen > PAGE_SIZE)
+ return (ENAMETOOLONG);
+
+ *dstp = dst = malloc(srclen + 1, M_TEMP|M_ZERO, M_WAITOK);
+ if (src != NULL) {
+ error = copyin(src, dst, srclen);
+ dst[srclen] = '\0';
+ }
+
+ return (error);
+}
+
+int
+pvbusioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
+{
+ struct pvbus_req *pvr = (struct pvbus_req *)data;
+ struct pvbus_softc *sc = pvbus_softc;
+ char *value = NULL, *key = NULL;
+ const char *str = NULL;
+ size_t valuelen = 0, keylen = 0, sz;
+ int hvid, error = 0, op;
+ struct pvbus_hv *hv;
+
+ if (sc == NULL)
+ return (ENODEV);
+ if ((hvid = pvbus_minor(sc, dev)) == -1)
+ return (ENXIO);
+
+ switch (cmd) {
+ case PVBUSIOC_KVWRITE:
+ if ((flags & FWRITE) == 0)
+ return (EPERM);
+ case PVBUSIOC_KVREAD:
+ hv = &sc->pvbus_hv[hvid];
+ if (hv->hv_base == 0 || hv->hv_kvop == NULL)
+ return (ENXIO);
+ break;
+ case PVBUSIOC_TYPE:
+ str = pvbus_types[hvid].name;
+ sz = strlen(str) + 1;
+ if (sz > pvr->pvr_keylen)
+ return (ENOMEM);
+ error = copyout(str, pvr->pvr_key, sz);
+ return (error);
+ default:
+ return (ENOTTY);
+ }
+
+ str = NULL;
+ op = PVBUS_KVREAD;
+
+ switch (cmd) {
+ case PVBUSIOC_KVWRITE:
+ str = pvr->pvr_value;
+ op = PVBUS_KVWRITE;
+
+ /* FALLTHROUGH */
+ case PVBUSIOC_KVREAD:
+ keylen = pvr->pvr_keylen;
+ if ((error = pvbusgetstr(keylen, pvr->pvr_key, &key)) != 0)
+ break;
+
+ valuelen = pvr->pvr_valuelen;
+ if ((error = pvbusgetstr(valuelen, str, &value)) != 0)
+ break;
+
+ /* Call driver-specific callback */
+ if ((error = (hv->hv_kvop)(hv->hv_arg, op,
+ key, value, valuelen)) != 0)
+ break;
+
+ sz = strlen(value) + 1;
+ if ((error = copyout(value, pvr->pvr_value, sz)) != 0)
+ break;
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+
+ free(key, M_TEMP, keylen);
+ free(value, M_TEMP, valuelen);
+
+ return (error);
+}
diff --git a/sys/dev/pv/pvvar.h b/sys/dev/pv/pvvar.h
index cd15cad63b1..4091c196130 100644
--- a/sys/dev/pv/pvvar.h
+++ b/sys/dev/pv/pvvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pvvar.h,v 1.6 2015/12/12 12:33:49 reyk Exp $ */
+/* $OpenBSD: pvvar.h,v 1.7 2016/01/27 09:04:19 reyk Exp $ */
/*
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -19,6 +19,18 @@
#ifndef _DEV_PV_PVVAR_H_
#define _DEV_PV_PVVAR_H_
+struct pvbus_req {
+ size_t pvr_keylen;
+ char *pvr_key;
+ size_t pvr_valuelen;
+ char *pvr_value;
+};
+
+#define PVBUSIOC_KVREAD _IOWR('V', 1, struct pvbus_req)
+#define PVBUSIOC_KVWRITE _IOWR('V', 2, struct pvbus_req)
+#define PVBUSIOC_TYPE _IOWR('V', 3, struct pvbus_req)
+
+#ifdef _KERNEL
enum {
PVBUS_KVM,
PVBUS_HYPERV,
@@ -30,11 +42,20 @@ enum {
PVBUS_MAX
};
+enum {
+ PVBUS_KVREAD,
+ PVBUS_KVWRITE,
+ PVBUS_KVLS
+};
+
struct pvbus_hv {
uint32_t hv_base;
uint32_t hv_features;
uint16_t hv_major;
uint16_t hv_minor;
+
+ void *hv_arg;
+ int (*hv_kvop)(void *, int, char *, char *, size_t);
};
struct pvbus_softc {
@@ -54,4 +75,5 @@ struct pv_attach_args {
void pvbus_identify(void);
int pvbus_probe(void);
+#endif /* _KERNEL */
#endif /* _DEV_PV_PVBUS_H_ */
diff --git a/sys/dev/pv/vmt.c b/sys/dev/pv/vmt.c
index 4333439bf92..a9b5dba82a8 100644
--- a/sys/dev/pv/vmt.c
+++ b/sys/dev/pv/vmt.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vmt.c,v 1.7 2015/12/17 09:26:36 mpi Exp $ */
+/* $OpenBSD: vmt.c,v 1.8 2016/01/27 09:04:19 reyk Exp $ */
/*
* Copyright (c) 2007 David Crawshaw <david@zentus.com>
@@ -35,6 +35,7 @@
#include <sys/syslog.h>
#include <sys/proc.h>
#include <sys/socket.h>
+#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_var.h>
@@ -166,7 +167,7 @@ struct vmt_softc {
int sc_rpc_error;
int sc_tclo_ping;
int sc_set_guest_os;
-#define VMT_RPC_BUFLEN 256
+#define VMT_RPC_BUFLEN 1024
struct timeout sc_tick;
struct timeout sc_tclo_tick;
@@ -199,6 +200,8 @@ int vm_rpc_send_rpci_tx(struct vmt_softc *, const char *, ...)
__attribute__((__format__(__kprintf__,2,3)));
int vm_rpci_response_successful(struct vmt_softc *);
+int vmt_kvop(void *, int, char *, char *, size_t);
+
void vmt_probe_cmd(struct vm_backdoor *, uint16_t);
void vmt_tclo_state_change_success(struct vmt_softc *, int, char);
void vmt_do_reboot(struct vmt_softc *);
@@ -334,6 +337,8 @@ void
vmt_attach(struct device *parent, struct device *self, void *aux)
{
struct vmt_softc *sc = (struct vmt_softc *)self;
+ struct pv_attach_args *pva = aux;
+ struct pvbus_hv *hv = &pva->pva_hv[PVBUS_VMWARE];
printf("\n");
sc->sc_rpc_buf = malloc(VMT_RPC_BUFLEN, M_DEVBUF, M_NOWAIT);
@@ -371,12 +376,62 @@ vmt_attach(struct device *parent, struct device *self, void *aux)
timeout_add_sec(&sc->sc_tclo_tick, 1);
sc->sc_tclo_ping = 1;
+ /* pvbus(4) key/value interface */
+ hv->hv_kvop = vmt_kvop;
+ hv->hv_arg = sc;
+
return;
free:
free(sc->sc_rpc_buf, M_DEVBUF, VMT_RPC_BUFLEN);
}
+int
+vmt_kvop(void *arg, int op, char *key, char *value, size_t valuelen)
+{
+ struct vmt_softc *sc = arg;
+ char buf[VMT_RPC_BUFLEN], *ptr;
+
+ switch (op) {
+ case PVBUS_KVWRITE:
+ if ((size_t)snprintf(buf, sizeof(buf), "info-set %s %s",
+ key, value) >= sizeof(buf)) {
+ DPRINTF("%s: write command too long", DEVNAME(sc));
+ return (EINVAL);
+ }
+ break;
+ case PVBUS_KVREAD:
+ if ((size_t)snprintf(buf, sizeof(buf), "info-get %s",
+ key) >= sizeof(buf)) {
+ DPRINTF("%s: read command too long", DEVNAME(sc));
+ return (EINVAL);
+ }
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ if (vm_rpc_send_rpci_tx(sc, buf) != 0) {
+ DPRINTF("%s: error sending command: %s\n", DEVNAME(sc), buf);
+ sc->sc_rpc_error = 1;
+ return (EIO);
+ }
+
+ if (vm_rpci_response_successful(sc) == 0) {
+ DPRINTF("%s: host rejected command: %s\n", DEVNAME(sc), buf);
+ return (EINVAL);
+ }
+
+ /* skip response that was tested in vm_rpci_response_successful() */
+ ptr = sc->sc_rpc_buf + 2;
+
+ /* might truncat, copy anyway but return error */
+ if (strlcpy(value, ptr, valuelen) >= valuelen)
+ return (ENOMEM);
+
+ return (0);
+}
+
void
vmt_resume(void)
{
diff --git a/sys/dev/pv/xen.c b/sys/dev/pv/xen.c
index 138e5e36c55..0ac3186bc69 100644
--- a/sys/dev/pv/xen.c
+++ b/sys/dev/pv/xen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: xen.c,v 1.39 2016/01/26 15:51:07 mikeb Exp $ */
+/* $OpenBSD: xen.c,v 1.40 2016/01/27 09:04:19 reyk Exp $ */
/*
* Copyright (c) 2015 Mike Belopuhov
@@ -156,6 +156,10 @@ xen_attach(struct device *parent, struct device *self, void *aux)
xen_probe_devices(sc);
+ /* pvbus(4) key/value interface */
+ hv->hv_kvop = xs_kvop;
+ hv->hv_arg = sc;
+
xen_disable_emulated_devices(sc);
config_mountroot(self, xen_deferred);
diff --git a/sys/dev/pv/xenstore.c b/sys/dev/pv/xenstore.c
index 021d6260f99..adfc52f1185 100644
--- a/sys/dev/pv/xenstore.c
+++ b/sys/dev/pv/xenstore.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: xenstore.c,v 1.21 2016/01/25 15:22:56 mikeb Exp $ */
+/* $OpenBSD: xenstore.c,v 1.22 2016/01/27 09:04:19 reyk Exp $ */
/*
* Copyright (c) 2015 Mike Belopuhov
@@ -23,11 +23,13 @@
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/mutex.h>
+#include <sys/ioctl.h>
#include <machine/bus.h>
#include <uvm/uvm_extern.h>
+#include <dev/pv/pvvar.h>
#include <dev/pv/xenreg.h>
#include <dev/pv/xenvar.h>
@@ -820,3 +822,69 @@ xs_setprop(struct xen_attach_args *xa, const char *property, char *value,
}
return (error);
}
+
+int
+xs_kvop(void *arg, int op, char *key, char *value, size_t valuelen)
+{
+ struct xen_softc *sc = arg;
+ struct xs_transaction xst;
+ struct iovec iov, *iovp = &iov;
+ int error = 0, iov_cnt, cmd, i;
+
+ switch (op) {
+ case PVBUS_KVWRITE:
+ cmd = XS_WRITE;
+ iov.iov_base = value;
+ iov.iov_len = strlen(value);
+ iov_cnt = 1;
+ break;
+ case PVBUS_KVREAD:
+ cmd = XS_READ;
+ break;
+ case PVBUS_KVLS:
+ cmd = XS_LIST;
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ memset(&xst, 0, sizeof(xst));
+ xst.xst_id = 0;
+ xst.xst_sc = sc->sc_xs;
+
+ if ((error = xs_cmd(&xst, cmd, key, &iovp, &iov_cnt)) != 0)
+ return (error);
+
+ memset(value, 0, valuelen);
+
+ switch (cmd) {
+ case XS_READ:
+ if (iov_cnt == 1 && iovp[0].iov_len == 1) {
+ xs_resfree(&xst, iovp, iov_cnt);
+
+ /*
+ * We cannot distinguish if the returned value is
+ * a directory or a file in the xenstore. The only
+ * indication is that the read value of a directory
+ * returns an empty string (single nul byte),
+ * so try to get the directory list in this case.
+ */
+ return (xs_kvop(arg, PVBUS_KVLS, key, value, valuelen));
+ }
+ /* FALLTHROUGH */
+ case XS_LIST:
+ for (i = 0; i < iov_cnt; i++) {
+ if (i && strlcat(value, "\n", valuelen) >= valuelen)
+ break;
+ if (strlcat(value, iovp[i].iov_base,
+ valuelen) >= valuelen)
+ break;
+ }
+ xs_resfree(&xst, iovp, iov_cnt);
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
diff --git a/sys/dev/pv/xenvar.h b/sys/dev/pv/xenvar.h
index bf2587e1550..3042e5b00e9 100644
--- a/sys/dev/pv/xenvar.h
+++ b/sys/dev/pv/xenvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: xenvar.h,v 1.23 2016/01/25 15:22:56 mikeb Exp $ */
+/* $OpenBSD: xenvar.h,v 1.24 2016/01/27 09:04:19 reyk Exp $ */
/*
* Copyright (c) 2015 Mike Belopuhov
@@ -150,5 +150,6 @@ int xs_cmd(struct xs_transaction *, int, const char *, struct iovec **,
int xs_getprop(struct xen_attach_args *, const char *, char *, int);
int xs_setprop(struct xen_attach_args *, const char *, char *, int);
void xs_resfree(struct xs_transaction *, struct iovec *, int);
+int xs_kvop(void *, int, char *, char *, size_t);
#endif /* _XENVAR_H_ */