diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2016-01-27 09:04:20 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2016-01-27 09:04:20 +0000 |
commit | 24f69262881c97ea25f1d4b8bb9616475a37900b (patch) | |
tree | 98fb61e459b195abc5491f25ba62d44ef834c990 /sys/dev | |
parent | d1e5520d3416e582a49f9a4c3bd7d63b5da7f401 (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.c | 147 | ||||
-rw-r--r-- | sys/dev/pv/pvvar.h | 24 | ||||
-rw-r--r-- | sys/dev/pv/vmt.c | 59 | ||||
-rw-r--r-- | sys/dev/pv/xen.c | 6 | ||||
-rw-r--r-- | sys/dev/pv/xenstore.c | 70 | ||||
-rw-r--r-- | sys/dev/pv/xenvar.h | 3 |
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_ */ |