Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/vmm/vmm_dev.c
Show First 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | struct devmem_softc { | ||||||||||
int segid; | int segid; | ||||||||||
char *name; | char *name; | ||||||||||
struct cdev *cdev; | struct cdev *cdev; | ||||||||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||||||||
SLIST_ENTRY(devmem_softc) link; | SLIST_ENTRY(devmem_softc) link; | ||||||||||
}; | }; | ||||||||||
struct vmmdev_softc { | struct vmmdev_softc { | ||||||||||
struct vm *vm; /* vm instance cookie */ | struct vm *vm; /* vm instance cookie */ | ||||||||||
struct cdev *cdev; | struct cdev *cdev; | ||||||||||
struct ucred *ucred; | struct ucred *ucred; | ||||||||||
SLIST_ENTRY(vmmdev_softc) link; | SLIST_ENTRY(vmmdev_softc) link; | ||||||||||
SLIST_HEAD(, devmem_softc) devmem; | SLIST_HEAD(, devmem_softc) devmem; | ||||||||||
int flags; | int flags; | ||||||||||
TAILQ_ENTRY(vmmdev_softc) fvmm_next; /* next entry in fvmm list */ | |||||||||||
bool persistent; /* persistent = created via sysctl */ | |||||||||||
}; | }; | ||||||||||
#define VSC_LINKED 0x01 | #define VSC_LINKED 0x01 | ||||||||||
static SLIST_HEAD(, vmmdev_softc) head; | static SLIST_HEAD(, vmmdev_softc) head; | ||||||||||
static unsigned pr_allow_flag; | static unsigned pr_allow_flag; | ||||||||||
static struct mtx vmmdev_mtx; | static struct mtx vmmdev_mtx; | ||||||||||
static unsigned vmmdev_prison_slot; | static unsigned vmmdev_prison_slot; | ||||||||||
▲ Show 20 Lines • Show All 960 Lines • ▼ Show 20 Lines | sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) | ||||||||||
strlcpy(buf, "beavis", buflen); | strlcpy(buf, "beavis", buflen); | ||||||||||
error = sysctl_handle_string(oidp, buf, buflen, req); | error = sysctl_handle_string(oidp, buf, buflen, req); | ||||||||||
if (error != 0 || req->newptr == NULL) | if (error != 0 || req->newptr == NULL) | ||||||||||
goto out; | goto out; | ||||||||||
mtx_lock(&vmmdev_mtx); | mtx_lock(&vmmdev_mtx); | ||||||||||
sc = vmmdev_lookup(buf); | sc = vmmdev_lookup(buf); | ||||||||||
if (!sc->persistent) { | |||||||||||
mtx_unlock(&vmmdev_mtx); | |||||||||||
error = EPERM; | |||||||||||
goto out; | |||||||||||
} | |||||||||||
error = vmm_destroy(sc); | error = vmm_destroy(sc); | ||||||||||
mtx_unlock(&vmmdev_mtx); | mtx_unlock(&vmmdev_mtx); | ||||||||||
out: | out: | ||||||||||
free(buf, M_VMMDEV); | free(buf, M_VMMDEV); | ||||||||||
return (error); | return (error); | ||||||||||
} | } | ||||||||||
SYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, | SYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, | ||||||||||
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, | ||||||||||
NULL, 0, sysctl_vmm_destroy, "A", | NULL, 0, sysctl_vmm_destroy, "A", | ||||||||||
NULL); | NULL); | ||||||||||
static struct cdevsw vmmdevsw = { | static struct cdevsw vmmdevsw = { | ||||||||||
.d_name = "vmmdev", | .d_name = "vmmdev", | ||||||||||
.d_version = D_VERSION, | .d_version = D_VERSION, | ||||||||||
.d_ioctl = vmmdev_ioctl, | .d_ioctl = vmmdev_ioctl, | ||||||||||
.d_mmap_single = vmmdev_mmap_single, | .d_mmap_single = vmmdev_mmap_single, | ||||||||||
.d_read = vmmdev_rw, | .d_read = vmmdev_rw, | ||||||||||
.d_write = vmmdev_rw, | .d_write = vmmdev_rw, | ||||||||||
}; | }; | ||||||||||
static int | static int | ||||||||||
sysctl_vmm_create(SYSCTL_HANDLER_ARGS) | vmm_create(const char *name, struct vmmdev_softc **sc) | ||||||||||
markj: I think `*name` can be const? | |||||||||||
{ | { | ||||||||||
struct vm *vm; | |||||||||||
struct cdev *cdev; | 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) { | |||||||||||
khngUnsubmitted Not Done Inline Actionsstyle-nit: there is no need to make one-line statement-true a compound statement. khng: style-nit: there is no need to make one-line statement-true a compound statement. | |||||||||||
return (EEXIST); | |||||||||||
} | |||||||||||
return (0); | |||||||||||
} | |||||||||||
static int | |||||||||||
sysctl_vmm_create(SYSCTL_HANDLER_ARGS) | |||||||||||
{ | |||||||||||
struct vmmdev_softc *sc; | |||||||||||
char *buf; | char *buf; | ||||||||||
int error; | int error; | ||||||||||
size_t buflen; | size_t buflen; | ||||||||||
error = vmm_priv_check(req->td->td_ucred); | error = vmm_priv_check(req->td->td_ucred); | ||||||||||
if (error) | if (error) | ||||||||||
return (error); | return (error); | ||||||||||
buflen = VM_MAX_NAMELEN + 1; | buflen = VM_MAX_NAMELEN + 1; | ||||||||||
buf = malloc(buflen, M_VMMDEV, M_WAITOK | M_ZERO); | buf = malloc(buflen, M_VMMDEV, M_WAITOK | M_ZERO); | ||||||||||
strlcpy(buf, "beavis", buflen); | strlcpy(buf, "beavis", buflen); | ||||||||||
error = sysctl_handle_string(oidp, buf, buflen, req); | error = sysctl_handle_string(oidp, buf, buflen, req); | ||||||||||
if (error != 0 || req->newptr == NULL) | if (error != 0 || req->newptr == NULL) | ||||||||||
goto out; | goto out; | ||||||||||
mtx_lock(&vmmdev_mtx); | |||||||||||
sc = vmmdev_lookup(buf); | sc = vmmdev_lookup(buf); | ||||||||||
mtx_unlock(&vmmdev_mtx); | |||||||||||
if (sc != NULL) { | if (sc != NULL) { | ||||||||||
error = EEXIST; | error = EEXIST; | ||||||||||
goto out; | goto out; | ||||||||||
} | } | ||||||||||
error = vm_create(buf, &vm); | error = vmm_create(buf, &sc); | ||||||||||
if (error != 0) | if (error != 0) | ||||||||||
goto out; | 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); | mtx_lock(&vmmdev_mtx); | ||||||||||
sc2 = vmmdev_lookup(buf); | error = vmm_add(sc); | ||||||||||
if (sc2 == NULL) { | |||||||||||
SLIST_INSERT_HEAD(&head, sc, link); | |||||||||||
sc->flags |= VSC_LINKED; | |||||||||||
} | |||||||||||
mtx_unlock(&vmmdev_mtx); | mtx_unlock(&vmmdev_mtx); | ||||||||||
if (error != 0) | |||||||||||
vmm_destroy(sc); | |||||||||||
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; | |||||||||||
mtx_unlock(&vmmdev_mtx); | |||||||||||
out: | out: | ||||||||||
free(buf, M_VMMDEV); | free(buf, M_VMMDEV); | ||||||||||
return (error); | return (error); | ||||||||||
} | } | ||||||||||
SYSCTL_PROC(_hw_vmm, OID_AUTO, create, | SYSCTL_PROC(_hw_vmm, OID_AUTO, create, | ||||||||||
CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, | CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, | ||||||||||
NULL, 0, sysctl_vmm_create, "A", | NULL, 0, sysctl_vmm_create, "A", | ||||||||||
NULL); | NULL); | ||||||||||
static void | |||||||||||
vmmdev_prison_cleanup(struct prison *pr) | |||||||||||
{ | |||||||||||
struct vmmdev_softc *sc; | |||||||||||
struct vmmdev_softc *tsc; | |||||||||||
mtx_lock(&vmmdev_mtx); | |||||||||||
SLIST_FOREACH_SAFE(sc, &head, link, tsc) { | |||||||||||
if (sc->ucred->cr_prison == pr) | |||||||||||
vmm_destroy(sc); | |||||||||||
} | |||||||||||
mtx_unlock(&vmmdev_mtx); | |||||||||||
} | |||||||||||
static int | static int | ||||||||||
vmmdev_prison_remove(void *obj, void *data __unused) | |||||||||||
{ | |||||||||||
struct prison *pr = obj; | |||||||||||
vmmdev_prison_cleanup(pr); | |||||||||||
return (0); | |||||||||||
} | |||||||||||
static int | |||||||||||
vmmdev_prison_set(void *obj, void *data) | |||||||||||
{ | |||||||||||
struct prison *pr = obj; | |||||||||||
struct vfsoptlist *opts = data; | |||||||||||
if (vfs_flagopt(opts, "allow.novmm", NULL, 0)) | |||||||||||
vmmdev_prison_cleanup(pr); | |||||||||||
return (0); | |||||||||||
} | |||||||||||
void | |||||||||||
vmmdev_init(void) | |||||||||||
{ | |||||||||||
osd_method_t methods[PR_MAXMETHOD] = { | |||||||||||
[PR_METHOD_SET] = vmmdev_prison_set, | |||||||||||
[PR_METHOD_REMOVE] = vmmdev_prison_remove, | |||||||||||
}; | |||||||||||
mtx_init(&vmmdev_mtx, "vmm device mutex", NULL, MTX_DEF); | |||||||||||
pr_allow_flag = prison_add_allow(NULL, "vmm", NULL, | |||||||||||
"Allow use of vmm in a jail."); | |||||||||||
vmmdev_prison_slot = osd_jail_register(NULL, methods); | |||||||||||
} | |||||||||||
int | |||||||||||
vmmdev_cleanup(void) | |||||||||||
{ | |||||||||||
int error; | |||||||||||
if (SLIST_EMPTY(&head)) { | |||||||||||
osd_jail_deregister(vmmdev_prison_slot); | |||||||||||
error = 0; | |||||||||||
} else { | |||||||||||
error = EBUSY; | |||||||||||
} | |||||||||||
return (error); | |||||||||||
} | |||||||||||
static int | |||||||||||
devmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t len, | devmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t len, | ||||||||||
struct vm_object **objp, int nprot) | struct vm_object **objp, int nprot) | ||||||||||
{ | { | ||||||||||
struct devmem_softc *dsc; | struct devmem_softc *dsc; | ||||||||||
vm_ooffset_t first, last; | vm_ooffset_t first, last; | ||||||||||
size_t seglen; | size_t seglen; | ||||||||||
int error; | int error; | ||||||||||
uint16_t lastcpu; | uint16_t lastcpu; | ||||||||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | |||||||||||
static void | static void | ||||||||||
devmem_destroy(void *arg) | devmem_destroy(void *arg) | ||||||||||
{ | { | ||||||||||
struct devmem_softc *dsc = arg; | struct devmem_softc *dsc = arg; | ||||||||||
KASSERT(dsc->cdev, ("%s: devmem cdev already destroyed", __func__)); | KASSERT(dsc->cdev, ("%s: devmem cdev already destroyed", __func__)); | ||||||||||
dsc->cdev = NULL; | dsc->cdev = NULL; | ||||||||||
dsc->sc = NULL; | dsc->sc = NULL; | ||||||||||
} | |||||||||||
struct fvmm { | |||||||||||
TAILQ_HEAD(softc_list, vmmdev_softc) scs; | |||||||||||
}; | |||||||||||
static int | |||||||||||
fvmm_destroy(struct fvmm *fvmm, char *name) | |||||||||||
khngUnsubmitted Not Done Inline Actions
khng: | |||||||||||
{ | |||||||||||
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; | |||||||||||
khngUnsubmitted Not Done Inline Actions
The namespace is still shared between vmmctl-created vm and sysctl-created vm. khng: The namespace is still shared between vmmctl-created vm and sysctl-created vm. | |||||||||||
} | |||||||||||
} | |||||||||||
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: | |||||||||||
Not Done Inline ActionsPer style(9), there should be no extra indentation for case statements. markj: Per style(9), there should be no extra indentation for `case` statements. | |||||||||||
op = (struct vmmctl_op *)data; | |||||||||||
if (strnlen(op->name, VM_MAX_NAMELEN) == VM_MAX_NAMELEN) { | |||||||||||
Not Done Inline Actionsop->name is a character buffer supplied by userland. Nothing guarantees that it is nul-terminated, so we should check that here, e.g., with strnlen(), and fail with EINVAL if not. markj: `op->name` is a character buffer supplied by userland. Nothing guarantees that it is nul… | |||||||||||
error = EINVAL; | |||||||||||
break; | |||||||||||
} | |||||||||||
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); | |||||||||||
Not Done Inline ActionsSame here regarding nul termination. markj: Same here regarding nul termination. | |||||||||||
mtx_unlock(&vmmdev_mtx); | |||||||||||
break; | |||||||||||
case VMMCTL_DESTROY: | |||||||||||
op = (struct vmmctl_op *)data; | |||||||||||
if (strnlen(op->name, VM_MAX_NAMELEN) == VM_MAX_NAMELEN) { | |||||||||||
khngUnsubmitted Not Done Inline ActionsVM_MAX_NAMELEN + 1 khng: VM_MAX_NAMELEN + 1 | |||||||||||
error = EINVAL; | |||||||||||
break; | |||||||||||
} | |||||||||||
error = fvmm_destroy(fvmm, op->name); | |||||||||||
break; | |||||||||||
} | |||||||||||
return (error); | |||||||||||
} | |||||||||||
static void | |||||||||||
vmmdev_prison_cleanup(struct prison *pr) | |||||||||||
{ | |||||||||||
struct vmmdev_softc *sc; | |||||||||||
struct vmmdev_softc *tsc; | |||||||||||
mtx_lock(&vmmdev_mtx); | |||||||||||
SLIST_FOREACH_SAFE(sc, &head, link, tsc) { | |||||||||||
if (sc->ucred->cr_prison == pr) | |||||||||||
vmm_destroy(sc); | |||||||||||
} | |||||||||||
mtx_unlock(&vmmdev_mtx); | |||||||||||
} | |||||||||||
Not Done Inline ActionsPerhaps loop over all VMs and fail if any are non-persistent? markj: Perhaps loop over all VMs and fail if any are non-persistent? | |||||||||||
static int | |||||||||||
vmmdev_prison_remove(void *obj, void *data __unused) | |||||||||||
{ | |||||||||||
struct prison *pr = obj; | |||||||||||
vmmdev_prison_cleanup(pr); | |||||||||||
return (0); | |||||||||||
} | |||||||||||
static int | |||||||||||
vmmdev_prison_set(void *obj, void *data) | |||||||||||
{ | |||||||||||
Not Done Inline ActionsI don't think there's any need to define a new module here? Can't we hook into vmmdev_init() and vmmdev_cleanup()? markj: I don't think there's any need to define a new module here? Can't we hook into `vmmdev_init()`… | |||||||||||
struct prison *pr = obj; | |||||||||||
struct vfsoptlist *opts = data; | |||||||||||
if (vfs_flagopt(opts, "allow.novmm", NULL, 0)) | |||||||||||
vmmdev_prison_cleanup(pr); | |||||||||||
return (0); | |||||||||||
} | |||||||||||
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; | |||||||||||
void | |||||||||||
vmmdev_init(void) | |||||||||||
{ | |||||||||||
osd_method_t methods[PR_MAXMETHOD] = { | |||||||||||
[PR_METHOD_SET] = vmmdev_prison_set, | |||||||||||
[PR_METHOD_REMOVE] = vmmdev_prison_remove, | |||||||||||
}; | |||||||||||
mtx_init(&vmmdev_mtx, "vmm device mutex", NULL, MTX_DEF); | |||||||||||
pr_allow_flag = prison_add_allow(NULL, "vmm", NULL, | |||||||||||
"Allow use of vmm in a jail."); | |||||||||||
vmmdev_prison_slot = osd_jail_register(NULL, methods); | |||||||||||
vmmctl_dev = make_dev(&vmmctl_cdevsw, 0, | |||||||||||
UID_ROOT, GID_WHEEL, 0600, "vmmctl"); | |||||||||||
} | |||||||||||
int | |||||||||||
vmmdev_cleanup(void) | |||||||||||
{ | |||||||||||
int error; | |||||||||||
if (SLIST_EMPTY(&head)) { | |||||||||||
osd_jail_deregister(vmmdev_prison_slot); | |||||||||||
destroy_dev(vmmctl_dev); | |||||||||||
error = 0; | |||||||||||
} else { | |||||||||||
error = EBUSY; | |||||||||||
} | |||||||||||
return (error); | |||||||||||
} | } |
I think *name can be const?