diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h --- a/lib/libvmmapi/vmmapi.h +++ b/lib/libvmmapi/vmmapi.h @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -113,6 +114,10 @@ int vm_munmap_memseg(struct vmctx *ctx, vm_paddr_t gpa, size_t len); +int vmmctl_open(); +int vm_fcreate(int fd, const char *name); +int vm_fdestroy(int fd, const char *name); + int vm_create(const char *name); int vm_get_device_fd(struct vmctx *ctx); struct vmctx *vm_open(const char *name); diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c --- a/lib/libvmmapi/vmmapi.c +++ b/lib/libvmmapi/vmmapi.c @@ -105,6 +105,44 @@ return (fd); } +int +vmmctl_open() +{ + /* Try to load vmm(4) */ + if (modfind("vmm") < 0) + kldload("vmm"); + + return (open("/dev/vmmctl", O_RDWR, 0)); +} + +int +vm_fcreate(int fd, const char *name) +{ + struct vmmctl_op op; + + if (strlen(name) >= VM_MAX_NAMELEN) { + errno = ENAMETOOLONG; + return (-1); + } + + strcpy(op.name, name); + return (ioctl(fd, VMMCTL_CREATE, &op)); +} + +int +vm_fdestroy(int fd, const char *name) +{ + struct vmmctl_op op; + + if (strlen(name) >= VM_MAX_NAMELEN) { + errno = ENAMETOOLONG; + return (-1); + } + + strcpy(op.name, name); + return (ioctl(fd, VMMCTL_DESTROY, &op)); +} + int vm_create(const char *name) { diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h --- a/sys/amd64/include/vmm_dev.h +++ b/sys/amd64/include/vmm_dev.h @@ -347,6 +347,15 @@ IOCNUM_RESTORE_TIME = 115 }; +struct vmmctl_op { + char name[VM_MAX_NAMELEN]; +}; + +#define VMMCTL_CREATE \ + _IOWR('v', 101, struct vmmctl_op) +#define VMMCTL_DESTROY \ + _IOWR('v', 102, struct vmmctl_op) + #define VM_RUN \ _IOWR('v', IOCNUM_RUN, struct vm_run) #define VM_SUSPEND \ diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c --- a/sys/amd64/vmm/vmm_dev.c +++ b/sys/amd64/vmm/vmm_dev.c @@ -79,12 +79,14 @@ }; struct vmmdev_softc { - struct vm *vm; /* vm instance cookie */ + struct vm *vm; /* vm instance cookie */ struct cdev *cdev; struct ucred *ucred; SLIST_ENTRY(vmmdev_softc) link; SLIST_HEAD(, devmem_softc) devmem; int flags; + TAILQ_ENTRY(vmmdev_softc) fvmm_next; /* next entry in fvmm list */ + bool persistent; /* persistent = created via sysctl */ }; #define VSC_LINKED 0x01 @@ -1061,6 +1063,11 @@ mtx_lock(&vmmdev_mtx); sc = vmmdev_lookup(buf); + if (!sc->persistent) { + mtx_unlock(&vmmdev_mtx); + error = EPERM; + goto out; + } error = vmm_destroy(sc); mtx_unlock(&vmmdev_mtx); @@ -1083,11 +1090,64 @@ }; static int -sysctl_vmm_create(SYSCTL_HANDLER_ARGS) +vmm_create(char *name, struct vmmdev_softc **sc) { - struct vm *vm; struct cdev *cdev; - struct vmmdev_softc *sc, *sc2; + struct vm *vm; + struct vmmdev_softc *tsc; + int error; + + error = vm_create(name, &vm); + if (error != 0) + return (error); + + tsc = malloc(sizeof(struct vmmdev_softc), M_VMMDEV, M_WAITOK | M_ZERO); + tsc->ucred = crhold(curthread->td_ucred); + tsc->vm = vm; + tsc->persistent = true; + SLIST_INIT(&tsc->devmem); + + error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, tsc->ucred, + UID_ROOT, GID_WHEEL, 0600, "vmm/%s", name); + if (error != 0) { + vmmdev_destroy(tsc); + return (error); + } + + tsc->cdev = cdev; + tsc->cdev->si_drv1 = tsc; + *sc = tsc; + + return (0); +} + +static int +vmm_add(struct vmmdev_softc *sc) +{ + struct vmmdev_softc *tsc; + + mtx_assert(&vmmdev_mtx, MA_OWNED); + + /* + * Lookup the name just in case somebody sneaked in. + */ + tsc = vmmdev_lookup(vm_name(sc->vm)); + if (tsc == NULL) { + SLIST_INSERT_HEAD(&head, sc, link); + sc->flags |= VSC_LINKED; + } + + if (tsc != NULL) { + return (EEXIST); + } + + return (0); +} + +static int +sysctl_vmm_create(SYSCTL_HANDLER_ARGS) +{ + struct vmmdev_softc *sc; char *buf; int error; size_t buflen; @@ -1103,52 +1163,21 @@ if (error != 0 || req->newptr == NULL) goto out; - mtx_lock(&vmmdev_mtx); sc = vmmdev_lookup(buf); - mtx_unlock(&vmmdev_mtx); if (sc != NULL) { error = EEXIST; goto out; } - error = vm_create(buf, &vm); + error = vmm_create(buf, &sc); if (error != 0) goto out; - sc = malloc(sizeof(struct vmmdev_softc), M_VMMDEV, M_WAITOK | M_ZERO); - sc->ucred = crhold(curthread->td_ucred); - sc->vm = vm; - SLIST_INIT(&sc->devmem); - - /* - * Lookup the name again just in case somebody sneaked in when we - * dropped the lock. - */ - mtx_lock(&vmmdev_mtx); - sc2 = vmmdev_lookup(buf); - if (sc2 == NULL) { - SLIST_INSERT_HEAD(&head, sc, link); - sc->flags |= VSC_LINKED; - } - mtx_unlock(&vmmdev_mtx); - - if (sc2 != NULL) { - vmmdev_destroy(sc); - error = EEXIST; - goto out; - } - - error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, sc->ucred, - UID_ROOT, GID_WHEEL, 0600, "vmm/%s", buf); - if (error != 0) { - vmmdev_destroy(sc); - goto out; - } - mtx_lock(&vmmdev_mtx); - sc->cdev = cdev; - sc->cdev->si_drv1 = sc; + error = vmm_add(sc); mtx_unlock(&vmmdev_mtx); + if (error != 0) + vmm_destroy(sc); out: free(buf, M_VMMDEV); @@ -1320,3 +1349,129 @@ dsc->cdev = NULL; dsc->sc = NULL; } + +struct fvmm { + TAILQ_HEAD(softc_list, vmmdev_softc) scs; +}; + +static int +fvmm_destroy(struct fvmm *fvmm, char *name) +{ + struct vmmdev_softc *sc, *tsc; + int error = ENOENT; + + mtx_lock(&vmmdev_mtx); + TAILQ_FOREACH_SAFE(sc, &fvmm->scs, fvmm_next, tsc) { + if (strcmp(vm_name(sc->vm), name) == 0) { + vmm_destroy(sc); + TAILQ_REMOVE(&fvmm->scs, sc, fvmm_next); + error = 0; + } + } + mtx_unlock(&vmmdev_mtx); + + return (error); +} + +static void +fvmm_dtor(void *data) +{ + struct fvmm *fvmm = data; + struct vmmdev_softc *sc; + + mtx_lock(&vmmdev_mtx); + while ((sc = TAILQ_FIRST(&fvmm->scs))) { + TAILQ_REMOVE(&fvmm->scs, sc, fvmm_next); + vmm_destroy(sc); + } + mtx_unlock(&vmmdev_mtx); + free(fvmm, M_VMMDEV); +} + +static int +vmmctl_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct fvmm *fvmm; + int error; + + fvmm = malloc(sizeof(struct fvmm), M_VMMDEV, M_WAITOK | M_ZERO); + TAILQ_INIT(&fvmm->scs); + error = devfs_set_cdevpriv(fvmm, fvmm_dtor); + if (error) + fvmm_dtor(fvmm); + return (error); +} + +static int +vmmctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, + struct thread *td) +{ + struct fvmm *fvmm; + struct vmmdev_softc *sc; + struct vmmctl_op *op; + int error = 0; + + devfs_get_cdevpriv((void **)&fvmm); + + switch (cmd) { + case VMMCTL_CREATE: + op = (struct vmmctl_op *)data; + error = vmm_create(op->name, &sc); + if (error) + break; + sc->persistent = false; + mtx_lock(&vmmdev_mtx); + error = vmm_add(sc); + if (error) { + vmm_destroy(sc); + mtx_unlock(&vmmdev_mtx); + break; + } + TAILQ_INSERT_HEAD(&fvmm->scs, sc, fvmm_next); + mtx_unlock(&vmmdev_mtx); + break; + case VMMCTL_DESTROY: + op = (struct vmmctl_op *)data; + error = fvmm_destroy(fvmm, op->name); + break; + } + + return (error); +} + +static struct cdevsw vmmctl_cdevsw = { + .d_version = D_VERSION, + .d_open = vmmctl_open, + .d_ioctl = vmmctl_ioctl, + .d_name = "vmmctl", +}; +static struct cdev *vmmctl_dev; + +/* + * Initialization code, both for static and dynamic loading. + */ +static int +vmmctl_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + if (bootverbose) + printf("vmmctl: \n"); + vmmctl_dev = make_dev(&vmmctl_cdevsw, 0, + UID_ROOT, GID_WHEEL, 0600, "vmmctl"); + return 0; + case MOD_UNLOAD: + /*XXX disallow if active sessions */ + destroy_dev(vmmctl_dev); + return 0; + } + return EINVAL; +} + +static moduledata_t vmmctl_mod = { + "vmmctl", + vmmctl_modevent, + 0 +}; +MODULE_VERSION(vmmctl, 1); +DECLARE_MODULE(vmmctl, vmmctl_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);