1b9ef152bSMark Johnston /*- 2b9ef152bSMark Johnston * SPDX-License-Identifier: BSD-2-Clause 3b9ef152bSMark Johnston * 4b9ef152bSMark Johnston * Copyright (c) 2011 NetApp, Inc. 5b9ef152bSMark Johnston * Copyright (C) 2015 Mihai Carabas <mihai.carabas@gmail.com> 6b9ef152bSMark Johnston * All rights reserved. 7b9ef152bSMark Johnston */ 8b9ef152bSMark Johnston 9b9ef152bSMark Johnston #include <sys/param.h> 10b9ef152bSMark Johnston #include <sys/conf.h> 11b9ef152bSMark Johnston #include <sys/ioccom.h> 12b9ef152bSMark Johnston #include <sys/jail.h> 13b9ef152bSMark Johnston #include <sys/kernel.h> 14b9ef152bSMark Johnston #include <sys/malloc.h> 15b9ef152bSMark Johnston #include <sys/mman.h> 16b9ef152bSMark Johnston #include <sys/proc.h> 17b9ef152bSMark Johnston #include <sys/queue.h> 18887c0877SMark Johnston #include <sys/sx.h> 19b9ef152bSMark Johnston #include <sys/sysctl.h> 20b9ef152bSMark Johnston #include <sys/ucred.h> 21b9ef152bSMark Johnston #include <sys/uio.h> 22b9ef152bSMark Johnston 23b9ef152bSMark Johnston #include <machine/vmm.h> 24b9ef152bSMark Johnston 25b9ef152bSMark Johnston #include <vm/vm.h> 26b9ef152bSMark Johnston #include <vm/vm_object.h> 27b9ef152bSMark Johnston 28b9ef152bSMark Johnston #include <dev/vmm/vmm_dev.h> 29b9ef152bSMark Johnston #include <dev/vmm/vmm_stat.h> 30b9ef152bSMark Johnston 31e12b6aafSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12) 32a852dc58SMark Johnston struct vm_memseg_12 { 33e12b6aafSMark Johnston int segid; 34e12b6aafSMark Johnston size_t len; 35e12b6aafSMark Johnston char name[64]; 36e12b6aafSMark Johnston }; 37a852dc58SMark Johnston _Static_assert(sizeof(struct vm_memseg_12) == 80, "COMPAT_FREEBSD12 ABI"); 38e12b6aafSMark Johnston 39a852dc58SMark Johnston #define VM_ALLOC_MEMSEG_12 \ 40a852dc58SMark Johnston _IOW('v', IOCNUM_ALLOC_MEMSEG, struct vm_memseg_12) 41a852dc58SMark Johnston #define VM_GET_MEMSEG_12 \ 42a852dc58SMark Johnston _IOWR('v', IOCNUM_GET_MEMSEG, struct vm_memseg_12) 43e12b6aafSMark Johnston #endif 44e12b6aafSMark Johnston 45b9ef152bSMark Johnston struct devmem_softc { 46b9ef152bSMark Johnston int segid; 47b9ef152bSMark Johnston char *name; 48b9ef152bSMark Johnston struct cdev *cdev; 49b9ef152bSMark Johnston struct vmmdev_softc *sc; 50b9ef152bSMark Johnston SLIST_ENTRY(devmem_softc) link; 51b9ef152bSMark Johnston }; 52b9ef152bSMark Johnston 53b9ef152bSMark Johnston struct vmmdev_softc { 54b9ef152bSMark Johnston struct vm *vm; /* vm instance cookie */ 55b9ef152bSMark Johnston struct cdev *cdev; 56b9ef152bSMark Johnston struct ucred *ucred; 57b9ef152bSMark Johnston SLIST_ENTRY(vmmdev_softc) link; 58b9ef152bSMark Johnston SLIST_HEAD(, devmem_softc) devmem; 59b9ef152bSMark Johnston int flags; 60b9ef152bSMark Johnston }; 61b9ef152bSMark Johnston 62b9ef152bSMark Johnston static SLIST_HEAD(, vmmdev_softc) head; 63b9ef152bSMark Johnston 64b9ef152bSMark Johnston static unsigned pr_allow_flag; 65887c0877SMark Johnston static struct sx vmmdev_mtx; 66887c0877SMark Johnston SX_SYSINIT(vmmdev_mtx, &vmmdev_mtx, "vmm device mutex"); 67b9ef152bSMark Johnston 68b9ef152bSMark Johnston static MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev"); 69b9ef152bSMark Johnston 70b9ef152bSMark Johnston SYSCTL_DECL(_hw_vmm); 71b9ef152bSMark Johnston 72b9ef152bSMark Johnston static void devmem_destroy(void *arg); 73f4002135SMark Johnston static int devmem_create_cdev(struct vmmdev_softc *sc, int id, char *devmem); 74b9ef152bSMark Johnston 75b9ef152bSMark Johnston static int 76b9ef152bSMark Johnston vmm_priv_check(struct ucred *ucred) 77b9ef152bSMark Johnston { 78b9ef152bSMark Johnston if (jailed(ucred) && 79b9ef152bSMark Johnston !(ucred->cr_prison->pr_allow & pr_allow_flag)) 80b9ef152bSMark Johnston return (EPERM); 81b9ef152bSMark Johnston 82b9ef152bSMark Johnston return (0); 83b9ef152bSMark Johnston } 84b9ef152bSMark Johnston 85b9ef152bSMark Johnston static int 86b9ef152bSMark Johnston vcpu_lock_one(struct vcpu *vcpu) 87b9ef152bSMark Johnston { 88b9ef152bSMark Johnston return (vcpu_set_state(vcpu, VCPU_FROZEN, true)); 89b9ef152bSMark Johnston } 90b9ef152bSMark Johnston 91b9ef152bSMark Johnston static void 92b9ef152bSMark Johnston vcpu_unlock_one(struct vcpu *vcpu) 93b9ef152bSMark Johnston { 94b9ef152bSMark Johnston enum vcpu_state state; 95b9ef152bSMark Johnston 96b9ef152bSMark Johnston state = vcpu_get_state(vcpu, NULL); 97b9ef152bSMark Johnston if (state != VCPU_FROZEN) { 98b9ef152bSMark Johnston panic("vcpu %s(%d) has invalid state %d", 99b9ef152bSMark Johnston vm_name(vcpu_vm(vcpu)), vcpu_vcpuid(vcpu), state); 100b9ef152bSMark Johnston } 101b9ef152bSMark Johnston 102b9ef152bSMark Johnston vcpu_set_state(vcpu, VCPU_IDLE, false); 103b9ef152bSMark Johnston } 104b9ef152bSMark Johnston 105b9ef152bSMark Johnston static int 106b9ef152bSMark Johnston vcpu_lock_all(struct vmmdev_softc *sc) 107b9ef152bSMark Johnston { 108b9ef152bSMark Johnston struct vcpu *vcpu; 109b9ef152bSMark Johnston int error; 110b9ef152bSMark Johnston uint16_t i, j, maxcpus; 111b9ef152bSMark Johnston 112b9ef152bSMark Johnston error = 0; 113b9ef152bSMark Johnston vm_slock_vcpus(sc->vm); 114b9ef152bSMark Johnston maxcpus = vm_get_maxcpus(sc->vm); 115b9ef152bSMark Johnston for (i = 0; i < maxcpus; i++) { 116b9ef152bSMark Johnston vcpu = vm_vcpu(sc->vm, i); 117b9ef152bSMark Johnston if (vcpu == NULL) 118b9ef152bSMark Johnston continue; 119b9ef152bSMark Johnston error = vcpu_lock_one(vcpu); 120b9ef152bSMark Johnston if (error) 121b9ef152bSMark Johnston break; 122b9ef152bSMark Johnston } 123b9ef152bSMark Johnston 124b9ef152bSMark Johnston if (error) { 125b9ef152bSMark Johnston for (j = 0; j < i; j++) { 126b9ef152bSMark Johnston vcpu = vm_vcpu(sc->vm, j); 127b9ef152bSMark Johnston if (vcpu == NULL) 128b9ef152bSMark Johnston continue; 129b9ef152bSMark Johnston vcpu_unlock_one(vcpu); 130b9ef152bSMark Johnston } 131b9ef152bSMark Johnston vm_unlock_vcpus(sc->vm); 132b9ef152bSMark Johnston } 133b9ef152bSMark Johnston 134b9ef152bSMark Johnston return (error); 135b9ef152bSMark Johnston } 136b9ef152bSMark Johnston 137b9ef152bSMark Johnston static void 138b9ef152bSMark Johnston vcpu_unlock_all(struct vmmdev_softc *sc) 139b9ef152bSMark Johnston { 140b9ef152bSMark Johnston struct vcpu *vcpu; 141b9ef152bSMark Johnston uint16_t i, maxcpus; 142b9ef152bSMark Johnston 143b9ef152bSMark Johnston maxcpus = vm_get_maxcpus(sc->vm); 144b9ef152bSMark Johnston for (i = 0; i < maxcpus; i++) { 145b9ef152bSMark Johnston vcpu = vm_vcpu(sc->vm, i); 146b9ef152bSMark Johnston if (vcpu == NULL) 147b9ef152bSMark Johnston continue; 148b9ef152bSMark Johnston vcpu_unlock_one(vcpu); 149b9ef152bSMark Johnston } 150b9ef152bSMark Johnston vm_unlock_vcpus(sc->vm); 151b9ef152bSMark Johnston } 152b9ef152bSMark Johnston 153b9ef152bSMark Johnston static struct vmmdev_softc * 154c23da668SMark Johnston vmmdev_lookup(const char *name, struct ucred *cred) 155b9ef152bSMark Johnston { 156b9ef152bSMark Johnston struct vmmdev_softc *sc; 157b9ef152bSMark Johnston 158887c0877SMark Johnston sx_assert(&vmmdev_mtx, SA_XLOCKED); 159b9ef152bSMark Johnston 160b9ef152bSMark Johnston SLIST_FOREACH(sc, &head, link) { 161b9ef152bSMark Johnston if (strcmp(name, vm_name(sc->vm)) == 0) 162b9ef152bSMark Johnston break; 163b9ef152bSMark Johnston } 164b9ef152bSMark Johnston 165b9ef152bSMark Johnston if (sc == NULL) 166b9ef152bSMark Johnston return (NULL); 167b9ef152bSMark Johnston 168c23da668SMark Johnston if (cr_cansee(cred, sc->ucred)) 169b9ef152bSMark Johnston return (NULL); 170b9ef152bSMark Johnston 171b9ef152bSMark Johnston return (sc); 172b9ef152bSMark Johnston } 173b9ef152bSMark Johnston 174b9ef152bSMark Johnston static struct vmmdev_softc * 175b9ef152bSMark Johnston vmmdev_lookup2(struct cdev *cdev) 176b9ef152bSMark Johnston { 177b9ef152bSMark Johnston return (cdev->si_drv1); 178b9ef152bSMark Johnston } 179b9ef152bSMark Johnston 180b9ef152bSMark Johnston static int 181b9ef152bSMark Johnston vmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) 182b9ef152bSMark Johnston { 183b9ef152bSMark Johnston int error, off, c, prot; 184b9ef152bSMark Johnston vm_paddr_t gpa, maxaddr; 185b9ef152bSMark Johnston void *hpa, *cookie; 186b9ef152bSMark Johnston struct vmmdev_softc *sc; 187b9ef152bSMark Johnston 188b9ef152bSMark Johnston sc = vmmdev_lookup2(cdev); 189b9ef152bSMark Johnston if (sc == NULL) 190b9ef152bSMark Johnston return (ENXIO); 191b9ef152bSMark Johnston 192b9ef152bSMark Johnston /* 193b9ef152bSMark Johnston * Get a read lock on the guest memory map. 194b9ef152bSMark Johnston */ 195b9ef152bSMark Johnston vm_slock_memsegs(sc->vm); 196b9ef152bSMark Johnston 197b9ef152bSMark Johnston prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ); 198b9ef152bSMark Johnston maxaddr = vmm_sysmem_maxaddr(sc->vm); 199b9ef152bSMark Johnston while (uio->uio_resid > 0 && error == 0) { 200b9ef152bSMark Johnston gpa = uio->uio_offset; 201b9ef152bSMark Johnston off = gpa & PAGE_MASK; 202b9ef152bSMark Johnston c = min(uio->uio_resid, PAGE_SIZE - off); 203b9ef152bSMark Johnston 204b9ef152bSMark Johnston /* 205b9ef152bSMark Johnston * The VM has a hole in its physical memory map. If we want to 206b9ef152bSMark Johnston * use 'dd' to inspect memory beyond the hole we need to 207b9ef152bSMark Johnston * provide bogus data for memory that lies in the hole. 208b9ef152bSMark Johnston * 209b9ef152bSMark Johnston * Since this device does not support lseek(2), dd(1) will 210b9ef152bSMark Johnston * read(2) blocks of data to simulate the lseek(2). 211b9ef152bSMark Johnston */ 212b9ef152bSMark Johnston hpa = vm_gpa_hold_global(sc->vm, gpa, c, prot, &cookie); 213b9ef152bSMark Johnston if (hpa == NULL) { 214b9ef152bSMark Johnston if (uio->uio_rw == UIO_READ && gpa < maxaddr) 215b9ef152bSMark Johnston error = uiomove(__DECONST(void *, zero_region), 216b9ef152bSMark Johnston c, uio); 217b9ef152bSMark Johnston else 218b9ef152bSMark Johnston error = EFAULT; 219b9ef152bSMark Johnston } else { 220b9ef152bSMark Johnston error = uiomove(hpa, c, uio); 221b9ef152bSMark Johnston vm_gpa_release(cookie); 222b9ef152bSMark Johnston } 223b9ef152bSMark Johnston } 224b9ef152bSMark Johnston vm_unlock_memsegs(sc->vm); 225b9ef152bSMark Johnston return (error); 226b9ef152bSMark Johnston } 227b9ef152bSMark Johnston 228b9ef152bSMark Johnston CTASSERT(sizeof(((struct vm_memseg *)0)->name) >= VM_MAX_SUFFIXLEN + 1); 229b9ef152bSMark Johnston 230b9ef152bSMark Johnston static int 231b9ef152bSMark Johnston get_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg, size_t len) 232b9ef152bSMark Johnston { 233b9ef152bSMark Johnston struct devmem_softc *dsc; 234b9ef152bSMark Johnston int error; 235b9ef152bSMark Johnston bool sysmem; 236b9ef152bSMark Johnston 237b9ef152bSMark Johnston error = vm_get_memseg(sc->vm, mseg->segid, &mseg->len, &sysmem, NULL); 238b9ef152bSMark Johnston if (error || mseg->len == 0) 239b9ef152bSMark Johnston return (error); 240b9ef152bSMark Johnston 241b9ef152bSMark Johnston if (!sysmem) { 242b9ef152bSMark Johnston SLIST_FOREACH(dsc, &sc->devmem, link) { 243b9ef152bSMark Johnston if (dsc->segid == mseg->segid) 244b9ef152bSMark Johnston break; 245b9ef152bSMark Johnston } 246b9ef152bSMark Johnston KASSERT(dsc != NULL, ("%s: devmem segment %d not found", 247b9ef152bSMark Johnston __func__, mseg->segid)); 248b9ef152bSMark Johnston error = copystr(dsc->name, mseg->name, len, NULL); 249b9ef152bSMark Johnston } else { 250b9ef152bSMark Johnston bzero(mseg->name, len); 251b9ef152bSMark Johnston } 252b9ef152bSMark Johnston 253b9ef152bSMark Johnston return (error); 254b9ef152bSMark Johnston } 255b9ef152bSMark Johnston 256b9ef152bSMark Johnston static int 257b9ef152bSMark Johnston alloc_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg, size_t len) 258b9ef152bSMark Johnston { 259b9ef152bSMark Johnston char *name; 260b9ef152bSMark Johnston int error; 261b9ef152bSMark Johnston bool sysmem; 262b9ef152bSMark Johnston 263b9ef152bSMark Johnston error = 0; 264b9ef152bSMark Johnston name = NULL; 265b9ef152bSMark Johnston sysmem = true; 266b9ef152bSMark Johnston 267b9ef152bSMark Johnston /* 268b9ef152bSMark Johnston * The allocation is lengthened by 1 to hold a terminating NUL. It'll 269b9ef152bSMark Johnston * by stripped off when devfs processes the full string. 270b9ef152bSMark Johnston */ 271b9ef152bSMark Johnston if (VM_MEMSEG_NAME(mseg)) { 272b9ef152bSMark Johnston sysmem = false; 273b9ef152bSMark Johnston name = malloc(len, M_VMMDEV, M_WAITOK); 274b9ef152bSMark Johnston error = copystr(mseg->name, name, len, NULL); 275b9ef152bSMark Johnston if (error) 276b9ef152bSMark Johnston goto done; 277b9ef152bSMark Johnston } 278b9ef152bSMark Johnston 279b9ef152bSMark Johnston error = vm_alloc_memseg(sc->vm, mseg->segid, mseg->len, sysmem); 280b9ef152bSMark Johnston if (error) 281b9ef152bSMark Johnston goto done; 282b9ef152bSMark Johnston 283b9ef152bSMark Johnston if (VM_MEMSEG_NAME(mseg)) { 284f4002135SMark Johnston error = devmem_create_cdev(sc, mseg->segid, name); 285b9ef152bSMark Johnston if (error) 286b9ef152bSMark Johnston vm_free_memseg(sc->vm, mseg->segid); 287b9ef152bSMark Johnston else 288b9ef152bSMark Johnston name = NULL; /* freed when 'cdev' is destroyed */ 289b9ef152bSMark Johnston } 290b9ef152bSMark Johnston done: 291b9ef152bSMark Johnston free(name, M_VMMDEV); 292b9ef152bSMark Johnston return (error); 293b9ef152bSMark Johnston } 294b9ef152bSMark Johnston 295b9ef152bSMark Johnston static int 296b9ef152bSMark Johnston vm_get_register_set(struct vcpu *vcpu, unsigned int count, int *regnum, 297b9ef152bSMark Johnston uint64_t *regval) 298b9ef152bSMark Johnston { 299b9ef152bSMark Johnston int error, i; 300b9ef152bSMark Johnston 301b9ef152bSMark Johnston error = 0; 302b9ef152bSMark Johnston for (i = 0; i < count; i++) { 303b9ef152bSMark Johnston error = vm_get_register(vcpu, regnum[i], ®val[i]); 304b9ef152bSMark Johnston if (error) 305b9ef152bSMark Johnston break; 306b9ef152bSMark Johnston } 307b9ef152bSMark Johnston return (error); 308b9ef152bSMark Johnston } 309b9ef152bSMark Johnston 310b9ef152bSMark Johnston static int 311b9ef152bSMark Johnston vm_set_register_set(struct vcpu *vcpu, unsigned int count, int *regnum, 312b9ef152bSMark Johnston uint64_t *regval) 313b9ef152bSMark Johnston { 314b9ef152bSMark Johnston int error, i; 315b9ef152bSMark Johnston 316b9ef152bSMark Johnston error = 0; 317b9ef152bSMark Johnston for (i = 0; i < count; i++) { 318b9ef152bSMark Johnston error = vm_set_register(vcpu, regnum[i], regval[i]); 319b9ef152bSMark Johnston if (error) 320b9ef152bSMark Johnston break; 321b9ef152bSMark Johnston } 322b9ef152bSMark Johnston return (error); 323b9ef152bSMark Johnston } 324b9ef152bSMark Johnston 32540087581SMark Johnston static int 32640087581SMark Johnston vmmdev_open(struct cdev *dev, int flags, int fmt, struct thread *td) 32740087581SMark Johnston { 32840087581SMark Johnston struct vmmdev_softc *sc; 32940087581SMark Johnston int error; 33040087581SMark Johnston 33140087581SMark Johnston sc = vmmdev_lookup2(dev); 33240087581SMark Johnston KASSERT(sc != NULL, ("%s: device not found", __func__)); 33340087581SMark Johnston 33440087581SMark Johnston /* 33540087581SMark Johnston * A user can only access VMs that they themselves have created. 33640087581SMark Johnston */ 33740087581SMark Johnston if (td->td_ucred != sc->ucred) 33840087581SMark Johnston return (EPERM); 33940087581SMark Johnston 34040087581SMark Johnston /* 34140087581SMark Johnston * A jail without vmm access shouldn't be able to access vmm device 34240087581SMark Johnston * files at all, but check here just to be thorough. 34340087581SMark Johnston */ 34440087581SMark Johnston error = vmm_priv_check(td->td_ucred); 34540087581SMark Johnston if (error != 0) 34640087581SMark Johnston return (error); 34740087581SMark Johnston 34840087581SMark Johnston return (0); 34940087581SMark Johnston } 35040087581SMark Johnston 351b9ef152bSMark Johnston static const struct vmmdev_ioctl vmmdev_ioctls[] = { 352b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_REGISTER, VMMDEV_IOCTL_LOCK_ONE_VCPU), 353b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SET_REGISTER, VMMDEV_IOCTL_LOCK_ONE_VCPU), 354b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_REGISTER_SET, VMMDEV_IOCTL_LOCK_ONE_VCPU), 355b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SET_REGISTER_SET, VMMDEV_IOCTL_LOCK_ONE_VCPU), 356b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_CAPABILITY, VMMDEV_IOCTL_LOCK_ONE_VCPU), 357b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SET_CAPABILITY, VMMDEV_IOCTL_LOCK_ONE_VCPU), 358b9ef152bSMark Johnston VMMDEV_IOCTL(VM_ACTIVATE_CPU, VMMDEV_IOCTL_LOCK_ONE_VCPU), 359b9ef152bSMark Johnston VMMDEV_IOCTL(VM_INJECT_EXCEPTION, VMMDEV_IOCTL_LOCK_ONE_VCPU), 360b9ef152bSMark Johnston VMMDEV_IOCTL(VM_STATS, VMMDEV_IOCTL_LOCK_ONE_VCPU), 361b9ef152bSMark Johnston 362b9ef152bSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12) 363a852dc58SMark Johnston VMMDEV_IOCTL(VM_ALLOC_MEMSEG_12, 364b9ef152bSMark Johnston VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS), 365b9ef152bSMark Johnston #endif 366b9ef152bSMark Johnston VMMDEV_IOCTL(VM_ALLOC_MEMSEG, 367b9ef152bSMark Johnston VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS), 368b9ef152bSMark Johnston VMMDEV_IOCTL(VM_MMAP_MEMSEG, 369b9ef152bSMark Johnston VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS), 370b9ef152bSMark Johnston VMMDEV_IOCTL(VM_MUNMAP_MEMSEG, 371b9ef152bSMark Johnston VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS), 372b9ef152bSMark Johnston VMMDEV_IOCTL(VM_REINIT, 373b9ef152bSMark Johnston VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS), 374b9ef152bSMark Johnston 375b9ef152bSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12) 376a852dc58SMark Johnston VMMDEV_IOCTL(VM_GET_MEMSEG_12, VMMDEV_IOCTL_SLOCK_MEMSEGS), 377b9ef152bSMark Johnston #endif 378b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_MEMSEG, VMMDEV_IOCTL_SLOCK_MEMSEGS), 379b9ef152bSMark Johnston VMMDEV_IOCTL(VM_MMAP_GETNEXT, VMMDEV_IOCTL_SLOCK_MEMSEGS), 380b9ef152bSMark Johnston 381b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SUSPEND_CPU, VMMDEV_IOCTL_MAYBE_ALLOC_VCPU), 382b9ef152bSMark Johnston VMMDEV_IOCTL(VM_RESUME_CPU, VMMDEV_IOCTL_MAYBE_ALLOC_VCPU), 383b9ef152bSMark Johnston 384b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SUSPEND, 0), 385b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_CPUS, 0), 386b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_TOPOLOGY, 0), 387b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SET_TOPOLOGY, 0), 388b9ef152bSMark Johnston }; 389b9ef152bSMark Johnston 390b9ef152bSMark Johnston static int 391b9ef152bSMark Johnston vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 392b9ef152bSMark Johnston struct thread *td) 393b9ef152bSMark Johnston { 394b9ef152bSMark Johnston struct vmmdev_softc *sc; 395b9ef152bSMark Johnston struct vcpu *vcpu; 396b9ef152bSMark Johnston const struct vmmdev_ioctl *ioctl; 397b9ef152bSMark Johnston int error, vcpuid; 398b9ef152bSMark Johnston 399b9ef152bSMark Johnston sc = vmmdev_lookup2(cdev); 400b9ef152bSMark Johnston if (sc == NULL) 401b9ef152bSMark Johnston return (ENXIO); 402b9ef152bSMark Johnston 403b9ef152bSMark Johnston ioctl = NULL; 404b9ef152bSMark Johnston for (size_t i = 0; i < nitems(vmmdev_ioctls); i++) { 405b9ef152bSMark Johnston if (vmmdev_ioctls[i].cmd == cmd) { 406b9ef152bSMark Johnston ioctl = &vmmdev_ioctls[i]; 407b9ef152bSMark Johnston break; 408b9ef152bSMark Johnston } 409b9ef152bSMark Johnston } 410b9ef152bSMark Johnston if (ioctl == NULL) { 411b9ef152bSMark Johnston for (size_t i = 0; i < vmmdev_machdep_ioctl_count; i++) { 412b9ef152bSMark Johnston if (vmmdev_machdep_ioctls[i].cmd == cmd) { 413b9ef152bSMark Johnston ioctl = &vmmdev_machdep_ioctls[i]; 414b9ef152bSMark Johnston break; 415b9ef152bSMark Johnston } 416b9ef152bSMark Johnston } 417b9ef152bSMark Johnston } 418b9ef152bSMark Johnston if (ioctl == NULL) 419b9ef152bSMark Johnston return (ENOTTY); 420b9ef152bSMark Johnston 421b9ef152bSMark Johnston if ((ioctl->flags & VMMDEV_IOCTL_XLOCK_MEMSEGS) != 0) 422b9ef152bSMark Johnston vm_xlock_memsegs(sc->vm); 423b9ef152bSMark Johnston else if ((ioctl->flags & VMMDEV_IOCTL_SLOCK_MEMSEGS) != 0) 424b9ef152bSMark Johnston vm_slock_memsegs(sc->vm); 425b9ef152bSMark Johnston 426b9ef152bSMark Johnston vcpu = NULL; 427b9ef152bSMark Johnston vcpuid = -1; 428b9ef152bSMark Johnston if ((ioctl->flags & (VMMDEV_IOCTL_LOCK_ONE_VCPU | 429b9ef152bSMark Johnston VMMDEV_IOCTL_ALLOC_VCPU | VMMDEV_IOCTL_MAYBE_ALLOC_VCPU)) != 0) { 430b9ef152bSMark Johnston vcpuid = *(int *)data; 431b9ef152bSMark Johnston if (vcpuid == -1) { 432b9ef152bSMark Johnston if ((ioctl->flags & 433b9ef152bSMark Johnston VMMDEV_IOCTL_MAYBE_ALLOC_VCPU) == 0) { 434b9ef152bSMark Johnston error = EINVAL; 435b9ef152bSMark Johnston goto lockfail; 436b9ef152bSMark Johnston } 437b9ef152bSMark Johnston } else { 438b9ef152bSMark Johnston vcpu = vm_alloc_vcpu(sc->vm, vcpuid); 439b9ef152bSMark Johnston if (vcpu == NULL) { 440b9ef152bSMark Johnston error = EINVAL; 441b9ef152bSMark Johnston goto lockfail; 442b9ef152bSMark Johnston } 443b9ef152bSMark Johnston if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ONE_VCPU) != 0) { 444b9ef152bSMark Johnston error = vcpu_lock_one(vcpu); 445b9ef152bSMark Johnston if (error) 446b9ef152bSMark Johnston goto lockfail; 447b9ef152bSMark Johnston } 448b9ef152bSMark Johnston } 449b9ef152bSMark Johnston } 450b9ef152bSMark Johnston if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ALL_VCPUS) != 0) { 451b9ef152bSMark Johnston error = vcpu_lock_all(sc); 452b9ef152bSMark Johnston if (error) 453b9ef152bSMark Johnston goto lockfail; 454b9ef152bSMark Johnston } 455b9ef152bSMark Johnston 456b9ef152bSMark Johnston switch (cmd) { 457b9ef152bSMark Johnston case VM_SUSPEND: { 458b9ef152bSMark Johnston struct vm_suspend *vmsuspend; 459b9ef152bSMark Johnston 460b9ef152bSMark Johnston vmsuspend = (struct vm_suspend *)data; 461b9ef152bSMark Johnston error = vm_suspend(sc->vm, vmsuspend->how); 462b9ef152bSMark Johnston break; 463b9ef152bSMark Johnston } 464b9ef152bSMark Johnston case VM_REINIT: 465b9ef152bSMark Johnston error = vm_reinit(sc->vm); 466b9ef152bSMark Johnston break; 467b9ef152bSMark Johnston case VM_STAT_DESC: { 468b9ef152bSMark Johnston struct vm_stat_desc *statdesc; 469b9ef152bSMark Johnston 470b9ef152bSMark Johnston statdesc = (struct vm_stat_desc *)data; 471b9ef152bSMark Johnston error = vmm_stat_desc_copy(statdesc->index, statdesc->desc, 472b9ef152bSMark Johnston sizeof(statdesc->desc)); 473b9ef152bSMark Johnston break; 474b9ef152bSMark Johnston } 475b9ef152bSMark Johnston case VM_STATS: { 476b9ef152bSMark Johnston struct vm_stats *vmstats; 477b9ef152bSMark Johnston 478b9ef152bSMark Johnston vmstats = (struct vm_stats *)data; 479b9ef152bSMark Johnston getmicrotime(&vmstats->tv); 480b9ef152bSMark Johnston error = vmm_stat_copy(vcpu, vmstats->index, 481b9ef152bSMark Johnston nitems(vmstats->statbuf), &vmstats->num_entries, 482b9ef152bSMark Johnston vmstats->statbuf); 483b9ef152bSMark Johnston break; 484b9ef152bSMark Johnston } 485b9ef152bSMark Johnston case VM_MMAP_GETNEXT: { 486b9ef152bSMark Johnston struct vm_memmap *mm; 487b9ef152bSMark Johnston 488b9ef152bSMark Johnston mm = (struct vm_memmap *)data; 489b9ef152bSMark Johnston error = vm_mmap_getnext(sc->vm, &mm->gpa, &mm->segid, 490b9ef152bSMark Johnston &mm->segoff, &mm->len, &mm->prot, &mm->flags); 491b9ef152bSMark Johnston break; 492b9ef152bSMark Johnston } 493b9ef152bSMark Johnston case VM_MMAP_MEMSEG: { 494b9ef152bSMark Johnston struct vm_memmap *mm; 495b9ef152bSMark Johnston 496b9ef152bSMark Johnston mm = (struct vm_memmap *)data; 497b9ef152bSMark Johnston error = vm_mmap_memseg(sc->vm, mm->gpa, mm->segid, mm->segoff, 498b9ef152bSMark Johnston mm->len, mm->prot, mm->flags); 499b9ef152bSMark Johnston break; 500b9ef152bSMark Johnston } 501b9ef152bSMark Johnston case VM_MUNMAP_MEMSEG: { 502b9ef152bSMark Johnston struct vm_munmap *mu; 503b9ef152bSMark Johnston 504b9ef152bSMark Johnston mu = (struct vm_munmap *)data; 505b9ef152bSMark Johnston error = vm_munmap_memseg(sc->vm, mu->gpa, mu->len); 506b9ef152bSMark Johnston break; 507b9ef152bSMark Johnston } 508b9ef152bSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12) 509a852dc58SMark Johnston case VM_ALLOC_MEMSEG_12: 510b9ef152bSMark Johnston error = alloc_memseg(sc, (struct vm_memseg *)data, 511a852dc58SMark Johnston sizeof(((struct vm_memseg_12 *)0)->name)); 512b9ef152bSMark Johnston break; 513a852dc58SMark Johnston case VM_GET_MEMSEG_12: 514b9ef152bSMark Johnston error = get_memseg(sc, (struct vm_memseg *)data, 515a852dc58SMark Johnston sizeof(((struct vm_memseg_12 *)0)->name)); 516b9ef152bSMark Johnston break; 517b9ef152bSMark Johnston #endif 518b9ef152bSMark Johnston case VM_ALLOC_MEMSEG: 519b9ef152bSMark Johnston error = alloc_memseg(sc, (struct vm_memseg *)data, 520b9ef152bSMark Johnston sizeof(((struct vm_memseg *)0)->name)); 521b9ef152bSMark Johnston break; 522b9ef152bSMark Johnston case VM_GET_MEMSEG: 523b9ef152bSMark Johnston error = get_memseg(sc, (struct vm_memseg *)data, 524b9ef152bSMark Johnston sizeof(((struct vm_memseg *)0)->name)); 525b9ef152bSMark Johnston break; 526b9ef152bSMark Johnston case VM_GET_REGISTER: { 527b9ef152bSMark Johnston struct vm_register *vmreg; 528b9ef152bSMark Johnston 529b9ef152bSMark Johnston vmreg = (struct vm_register *)data; 530b9ef152bSMark Johnston error = vm_get_register(vcpu, vmreg->regnum, &vmreg->regval); 531b9ef152bSMark Johnston break; 532b9ef152bSMark Johnston } 533b9ef152bSMark Johnston case VM_SET_REGISTER: { 534b9ef152bSMark Johnston struct vm_register *vmreg; 535b9ef152bSMark Johnston 536b9ef152bSMark Johnston vmreg = (struct vm_register *)data; 537b9ef152bSMark Johnston error = vm_set_register(vcpu, vmreg->regnum, vmreg->regval); 538b9ef152bSMark Johnston break; 539b9ef152bSMark Johnston } 540b9ef152bSMark Johnston case VM_GET_REGISTER_SET: { 541b9ef152bSMark Johnston struct vm_register_set *vmregset; 542b9ef152bSMark Johnston uint64_t *regvals; 543b9ef152bSMark Johnston int *regnums; 544b9ef152bSMark Johnston 545b9ef152bSMark Johnston vmregset = (struct vm_register_set *)data; 546b9ef152bSMark Johnston if (vmregset->count > VM_REG_LAST) { 547b9ef152bSMark Johnston error = EINVAL; 548b9ef152bSMark Johnston break; 549b9ef152bSMark Johnston } 550b9ef152bSMark Johnston regvals = malloc(sizeof(regvals[0]) * vmregset->count, M_VMMDEV, 551b9ef152bSMark Johnston M_WAITOK); 552b9ef152bSMark Johnston regnums = malloc(sizeof(regnums[0]) * vmregset->count, M_VMMDEV, 553b9ef152bSMark Johnston M_WAITOK); 554b9ef152bSMark Johnston error = copyin(vmregset->regnums, regnums, sizeof(regnums[0]) * 555b9ef152bSMark Johnston vmregset->count); 556b9ef152bSMark Johnston if (error == 0) 557b9ef152bSMark Johnston error = vm_get_register_set(vcpu, 558b9ef152bSMark Johnston vmregset->count, regnums, regvals); 559b9ef152bSMark Johnston if (error == 0) 560b9ef152bSMark Johnston error = copyout(regvals, vmregset->regvals, 561b9ef152bSMark Johnston sizeof(regvals[0]) * vmregset->count); 562b9ef152bSMark Johnston free(regvals, M_VMMDEV); 563b9ef152bSMark Johnston free(regnums, M_VMMDEV); 564b9ef152bSMark Johnston break; 565b9ef152bSMark Johnston } 566b9ef152bSMark Johnston case VM_SET_REGISTER_SET: { 567b9ef152bSMark Johnston struct vm_register_set *vmregset; 568b9ef152bSMark Johnston uint64_t *regvals; 569b9ef152bSMark Johnston int *regnums; 570b9ef152bSMark Johnston 571b9ef152bSMark Johnston vmregset = (struct vm_register_set *)data; 572b9ef152bSMark Johnston if (vmregset->count > VM_REG_LAST) { 573b9ef152bSMark Johnston error = EINVAL; 574b9ef152bSMark Johnston break; 575b9ef152bSMark Johnston } 576b9ef152bSMark Johnston regvals = malloc(sizeof(regvals[0]) * vmregset->count, M_VMMDEV, 577b9ef152bSMark Johnston M_WAITOK); 578b9ef152bSMark Johnston regnums = malloc(sizeof(regnums[0]) * vmregset->count, M_VMMDEV, 579b9ef152bSMark Johnston M_WAITOK); 580b9ef152bSMark Johnston error = copyin(vmregset->regnums, regnums, sizeof(regnums[0]) * 581b9ef152bSMark Johnston vmregset->count); 582b9ef152bSMark Johnston if (error == 0) 583b9ef152bSMark Johnston error = copyin(vmregset->regvals, regvals, 584b9ef152bSMark Johnston sizeof(regvals[0]) * vmregset->count); 585b9ef152bSMark Johnston if (error == 0) 586b9ef152bSMark Johnston error = vm_set_register_set(vcpu, 587b9ef152bSMark Johnston vmregset->count, regnums, regvals); 588b9ef152bSMark Johnston free(regvals, M_VMMDEV); 589b9ef152bSMark Johnston free(regnums, M_VMMDEV); 590b9ef152bSMark Johnston break; 591b9ef152bSMark Johnston } 592b9ef152bSMark Johnston case VM_GET_CAPABILITY: { 593b9ef152bSMark Johnston struct vm_capability *vmcap; 594b9ef152bSMark Johnston 595b9ef152bSMark Johnston vmcap = (struct vm_capability *)data; 596b9ef152bSMark Johnston error = vm_get_capability(vcpu, vmcap->captype, &vmcap->capval); 597b9ef152bSMark Johnston break; 598b9ef152bSMark Johnston } 599b9ef152bSMark Johnston case VM_SET_CAPABILITY: { 600b9ef152bSMark Johnston struct vm_capability *vmcap; 601b9ef152bSMark Johnston 602b9ef152bSMark Johnston vmcap = (struct vm_capability *)data; 603b9ef152bSMark Johnston error = vm_set_capability(vcpu, vmcap->captype, vmcap->capval); 604b9ef152bSMark Johnston break; 605b9ef152bSMark Johnston } 606b9ef152bSMark Johnston case VM_ACTIVATE_CPU: 607b9ef152bSMark Johnston error = vm_activate_cpu(vcpu); 608b9ef152bSMark Johnston break; 609b9ef152bSMark Johnston case VM_GET_CPUS: { 610b9ef152bSMark Johnston struct vm_cpuset *vm_cpuset; 611b9ef152bSMark Johnston cpuset_t *cpuset; 612b9ef152bSMark Johnston int size; 613b9ef152bSMark Johnston 614b9ef152bSMark Johnston error = 0; 615b9ef152bSMark Johnston vm_cpuset = (struct vm_cpuset *)data; 616b9ef152bSMark Johnston size = vm_cpuset->cpusetsize; 617b9ef152bSMark Johnston if (size < 1 || size > CPU_MAXSIZE / NBBY) { 618b9ef152bSMark Johnston error = ERANGE; 619b9ef152bSMark Johnston break; 620b9ef152bSMark Johnston } 621b9ef152bSMark Johnston cpuset = malloc(max(size, sizeof(cpuset_t)), M_TEMP, 622b9ef152bSMark Johnston M_WAITOK | M_ZERO); 623b9ef152bSMark Johnston if (vm_cpuset->which == VM_ACTIVE_CPUS) 624b9ef152bSMark Johnston *cpuset = vm_active_cpus(sc->vm); 625b9ef152bSMark Johnston else if (vm_cpuset->which == VM_SUSPENDED_CPUS) 626b9ef152bSMark Johnston *cpuset = vm_suspended_cpus(sc->vm); 627b9ef152bSMark Johnston else if (vm_cpuset->which == VM_DEBUG_CPUS) 628b9ef152bSMark Johnston *cpuset = vm_debug_cpus(sc->vm); 629b9ef152bSMark Johnston else 630b9ef152bSMark Johnston error = EINVAL; 631b9ef152bSMark Johnston if (error == 0 && size < howmany(CPU_FLS(cpuset), NBBY)) 632b9ef152bSMark Johnston error = ERANGE; 633b9ef152bSMark Johnston if (error == 0) 634b9ef152bSMark Johnston error = copyout(cpuset, vm_cpuset->cpus, size); 635b9ef152bSMark Johnston free(cpuset, M_TEMP); 636b9ef152bSMark Johnston break; 637b9ef152bSMark Johnston } 638b9ef152bSMark Johnston case VM_SUSPEND_CPU: 639b9ef152bSMark Johnston error = vm_suspend_cpu(sc->vm, vcpu); 640b9ef152bSMark Johnston break; 641b9ef152bSMark Johnston case VM_RESUME_CPU: 642b9ef152bSMark Johnston error = vm_resume_cpu(sc->vm, vcpu); 643b9ef152bSMark Johnston break; 644b9ef152bSMark Johnston case VM_SET_TOPOLOGY: { 645b9ef152bSMark Johnston struct vm_cpu_topology *topology; 646b9ef152bSMark Johnston 647b9ef152bSMark Johnston topology = (struct vm_cpu_topology *)data; 648b9ef152bSMark Johnston error = vm_set_topology(sc->vm, topology->sockets, 649b9ef152bSMark Johnston topology->cores, topology->threads, topology->maxcpus); 650b9ef152bSMark Johnston break; 651b9ef152bSMark Johnston } 652b9ef152bSMark Johnston case VM_GET_TOPOLOGY: { 653b9ef152bSMark Johnston struct vm_cpu_topology *topology; 654b9ef152bSMark Johnston 655b9ef152bSMark Johnston topology = (struct vm_cpu_topology *)data; 656b9ef152bSMark Johnston vm_get_topology(sc->vm, &topology->sockets, &topology->cores, 657b9ef152bSMark Johnston &topology->threads, &topology->maxcpus); 658b9ef152bSMark Johnston error = 0; 659b9ef152bSMark Johnston break; 660b9ef152bSMark Johnston } 661b9ef152bSMark Johnston default: 662b9ef152bSMark Johnston error = vmmdev_machdep_ioctl(sc->vm, vcpu, cmd, data, fflag, 663b9ef152bSMark Johnston td); 664b9ef152bSMark Johnston break; 665b9ef152bSMark Johnston } 666b9ef152bSMark Johnston 667b9ef152bSMark Johnston if ((ioctl->flags & 668b9ef152bSMark Johnston (VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_SLOCK_MEMSEGS)) != 0) 669b9ef152bSMark Johnston vm_unlock_memsegs(sc->vm); 670b9ef152bSMark Johnston if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ALL_VCPUS) != 0) 671b9ef152bSMark Johnston vcpu_unlock_all(sc); 672b9ef152bSMark Johnston else if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ONE_VCPU) != 0) 673b9ef152bSMark Johnston vcpu_unlock_one(vcpu); 674b9ef152bSMark Johnston 675b9ef152bSMark Johnston /* 676b9ef152bSMark Johnston * Make sure that no handler returns a kernel-internal 677b9ef152bSMark Johnston * error value to userspace. 678b9ef152bSMark Johnston */ 679b9ef152bSMark Johnston KASSERT(error == ERESTART || error >= 0, 680b9ef152bSMark Johnston ("vmmdev_ioctl: invalid error return %d", error)); 681b9ef152bSMark Johnston return (error); 682b9ef152bSMark Johnston 683b9ef152bSMark Johnston lockfail: 684b9ef152bSMark Johnston if ((ioctl->flags & 685b9ef152bSMark Johnston (VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_SLOCK_MEMSEGS)) != 0) 686b9ef152bSMark Johnston vm_unlock_memsegs(sc->vm); 687b9ef152bSMark Johnston return (error); 688b9ef152bSMark Johnston } 689b9ef152bSMark Johnston 690b9ef152bSMark Johnston static int 691b9ef152bSMark Johnston vmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t mapsize, 692b9ef152bSMark Johnston struct vm_object **objp, int nprot) 693b9ef152bSMark Johnston { 694b9ef152bSMark Johnston struct vmmdev_softc *sc; 695b9ef152bSMark Johnston vm_paddr_t gpa; 696b9ef152bSMark Johnston size_t len; 697b9ef152bSMark Johnston vm_ooffset_t segoff, first, last; 698b9ef152bSMark Johnston int error, found, segid; 699b9ef152bSMark Johnston bool sysmem; 700b9ef152bSMark Johnston 701b9ef152bSMark Johnston first = *offset; 702b9ef152bSMark Johnston last = first + mapsize; 703b9ef152bSMark Johnston if ((nprot & PROT_EXEC) || first < 0 || first >= last) 704b9ef152bSMark Johnston return (EINVAL); 705b9ef152bSMark Johnston 706b9ef152bSMark Johnston sc = vmmdev_lookup2(cdev); 707b9ef152bSMark Johnston if (sc == NULL) { 708b9ef152bSMark Johnston /* virtual machine is in the process of being created */ 709b9ef152bSMark Johnston return (EINVAL); 710b9ef152bSMark Johnston } 711b9ef152bSMark Johnston 712b9ef152bSMark Johnston /* 713b9ef152bSMark Johnston * Get a read lock on the guest memory map. 714b9ef152bSMark Johnston */ 715b9ef152bSMark Johnston vm_slock_memsegs(sc->vm); 716b9ef152bSMark Johnston 717b9ef152bSMark Johnston gpa = 0; 718b9ef152bSMark Johnston found = 0; 719b9ef152bSMark Johnston while (!found) { 720b9ef152bSMark Johnston error = vm_mmap_getnext(sc->vm, &gpa, &segid, &segoff, &len, 721b9ef152bSMark Johnston NULL, NULL); 722b9ef152bSMark Johnston if (error) 723b9ef152bSMark Johnston break; 724b9ef152bSMark Johnston 725b9ef152bSMark Johnston if (first >= gpa && last <= gpa + len) 726b9ef152bSMark Johnston found = 1; 727b9ef152bSMark Johnston else 728b9ef152bSMark Johnston gpa += len; 729b9ef152bSMark Johnston } 730b9ef152bSMark Johnston 731b9ef152bSMark Johnston if (found) { 732b9ef152bSMark Johnston error = vm_get_memseg(sc->vm, segid, &len, &sysmem, objp); 733b9ef152bSMark Johnston KASSERT(error == 0 && *objp != NULL, 734b9ef152bSMark Johnston ("%s: invalid memory segment %d", __func__, segid)); 735b9ef152bSMark Johnston if (sysmem) { 736b9ef152bSMark Johnston vm_object_reference(*objp); 737b9ef152bSMark Johnston *offset = segoff + (first - gpa); 738b9ef152bSMark Johnston } else { 739b9ef152bSMark Johnston error = EINVAL; 740b9ef152bSMark Johnston } 741b9ef152bSMark Johnston } 742b9ef152bSMark Johnston vm_unlock_memsegs(sc->vm); 743b9ef152bSMark Johnston return (error); 744b9ef152bSMark Johnston } 745b9ef152bSMark Johnston 746b9ef152bSMark Johnston static void 747063a8bd9SMark Johnston vmmdev_destroy(struct vmmdev_softc *sc) 748b9ef152bSMark Johnston { 749b9ef152bSMark Johnston struct devmem_softc *dsc; 750b9ef152bSMark Johnston int error __diagused; 751b9ef152bSMark Johnston 752*cef5f43fSMark Johnston KASSERT(sc->cdev == NULL, ("%s: cdev not free", __func__)); 753*cef5f43fSMark Johnston 754063a8bd9SMark Johnston /* 755063a8bd9SMark Johnston * Destroy all cdevs: 756063a8bd9SMark Johnston * 757063a8bd9SMark Johnston * - any new operations on the 'cdev' will return an error (ENXIO). 758063a8bd9SMark Johnston * 759063a8bd9SMark Johnston * - the 'devmem' cdevs are destroyed before the virtual machine 'cdev' 760063a8bd9SMark Johnston */ 761063a8bd9SMark Johnston SLIST_FOREACH(dsc, &sc->devmem, link) { 762063a8bd9SMark Johnston KASSERT(dsc->cdev != NULL, ("devmem cdev already destroyed")); 763063a8bd9SMark Johnston devmem_destroy(dsc); 764063a8bd9SMark Johnston } 765063a8bd9SMark Johnston 766b9ef152bSMark Johnston vm_disable_vcpu_creation(sc->vm); 767b9ef152bSMark Johnston error = vcpu_lock_all(sc); 768b9ef152bSMark Johnston KASSERT(error == 0, ("%s: error %d freezing vcpus", __func__, error)); 769b9ef152bSMark Johnston vm_unlock_vcpus(sc->vm); 770b9ef152bSMark Johnston 771b9ef152bSMark Johnston while ((dsc = SLIST_FIRST(&sc->devmem)) != NULL) { 772b9ef152bSMark Johnston KASSERT(dsc->cdev == NULL, ("%s: devmem not free", __func__)); 773b9ef152bSMark Johnston SLIST_REMOVE_HEAD(&sc->devmem, link); 774b9ef152bSMark Johnston free(dsc->name, M_VMMDEV); 775b9ef152bSMark Johnston free(dsc, M_VMMDEV); 776b9ef152bSMark Johnston } 777b9ef152bSMark Johnston 778b9ef152bSMark Johnston if (sc->vm != NULL) 779b9ef152bSMark Johnston vm_destroy(sc->vm); 780b9ef152bSMark Johnston 781b9ef152bSMark Johnston if (sc->ucred != NULL) 782b9ef152bSMark Johnston crfree(sc->ucred); 783b9ef152bSMark Johnston 784887c0877SMark Johnston sx_xlock(&vmmdev_mtx); 785b9ef152bSMark Johnston SLIST_REMOVE(&head, sc, vmmdev_softc, link); 786887c0877SMark Johnston sx_xunlock(&vmmdev_mtx); 787b9ef152bSMark Johnston free(sc, M_VMMDEV); 788b9ef152bSMark Johnston } 789b9ef152bSMark Johnston 790b9ef152bSMark Johnston static int 791063a8bd9SMark Johnston vmmdev_lookup_and_destroy(const char *name, struct ucred *cred) 792063a8bd9SMark Johnston { 793063a8bd9SMark Johnston struct cdev *cdev; 794063a8bd9SMark Johnston struct vmmdev_softc *sc; 795063a8bd9SMark Johnston 796887c0877SMark Johnston sx_xlock(&vmmdev_mtx); 797c23da668SMark Johnston sc = vmmdev_lookup(name, cred); 798063a8bd9SMark Johnston if (sc == NULL || sc->cdev == NULL) { 799887c0877SMark Johnston sx_xunlock(&vmmdev_mtx); 800063a8bd9SMark Johnston return (EINVAL); 801063a8bd9SMark Johnston } 802063a8bd9SMark Johnston 803063a8bd9SMark Johnston /* 804063a8bd9SMark Johnston * Setting 'sc->cdev' to NULL is used to indicate that the VM 805063a8bd9SMark Johnston * is scheduled for destruction. 806063a8bd9SMark Johnston */ 807063a8bd9SMark Johnston cdev = sc->cdev; 808063a8bd9SMark Johnston sc->cdev = NULL; 809887c0877SMark Johnston sx_xunlock(&vmmdev_mtx); 810063a8bd9SMark Johnston 811063a8bd9SMark Johnston destroy_dev(cdev); 812063a8bd9SMark Johnston vmmdev_destroy(sc); 813063a8bd9SMark Johnston 814063a8bd9SMark Johnston return (0); 815063a8bd9SMark Johnston } 816063a8bd9SMark Johnston 817063a8bd9SMark Johnston static int 818b9ef152bSMark Johnston sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) 819b9ef152bSMark Johnston { 820b9ef152bSMark Johnston char *buf; 821b9ef152bSMark Johnston int error, buflen; 822b9ef152bSMark Johnston 823b9ef152bSMark Johnston error = vmm_priv_check(req->td->td_ucred); 824b9ef152bSMark Johnston if (error) 825b9ef152bSMark Johnston return (error); 826b9ef152bSMark Johnston 827b9ef152bSMark Johnston buflen = VM_MAX_NAMELEN + 1; 828b9ef152bSMark Johnston buf = malloc(buflen, M_VMMDEV, M_WAITOK | M_ZERO); 829b9ef152bSMark Johnston strlcpy(buf, "beavis", buflen); 830b9ef152bSMark Johnston error = sysctl_handle_string(oidp, buf, buflen, req); 831063a8bd9SMark Johnston if (error == 0 && req->newptr != NULL) 832063a8bd9SMark Johnston error = vmmdev_lookup_and_destroy(buf, req->td->td_ucred); 833b9ef152bSMark Johnston free(buf, M_VMMDEV); 834b9ef152bSMark Johnston return (error); 835b9ef152bSMark Johnston } 836b9ef152bSMark Johnston SYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, 837b9ef152bSMark Johnston CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 838b9ef152bSMark Johnston NULL, 0, sysctl_vmm_destroy, "A", 839b9ef152bSMark Johnston NULL); 840b9ef152bSMark Johnston 841b9ef152bSMark Johnston static struct cdevsw vmmdevsw = { 842b9ef152bSMark Johnston .d_name = "vmmdev", 843b9ef152bSMark Johnston .d_version = D_VERSION, 84440087581SMark Johnston .d_open = vmmdev_open, 845b9ef152bSMark Johnston .d_ioctl = vmmdev_ioctl, 846b9ef152bSMark Johnston .d_mmap_single = vmmdev_mmap_single, 847b9ef152bSMark Johnston .d_read = vmmdev_rw, 848b9ef152bSMark Johnston .d_write = vmmdev_rw, 849b9ef152bSMark Johnston }; 850b9ef152bSMark Johnston 851d5819709SMark Johnston static struct vmmdev_softc * 852d5819709SMark Johnston vmmdev_alloc(struct vm *vm, struct ucred *cred) 853b9ef152bSMark Johnston { 854d5819709SMark Johnston struct vmmdev_softc *sc; 855b9ef152bSMark Johnston 856d5819709SMark Johnston sc = malloc(sizeof(*sc), M_VMMDEV, M_WAITOK | M_ZERO); 857d5819709SMark Johnston SLIST_INIT(&sc->devmem); 858d5819709SMark Johnston sc->vm = vm; 859d5819709SMark Johnston sc->ucred = crhold(cred); 860d5819709SMark Johnston return (sc); 861b9ef152bSMark Johnston } 862b9ef152bSMark Johnston 863d5819709SMark Johnston static int 864d5819709SMark Johnston vmmdev_create(const char *name, struct ucred *cred) 865d5819709SMark Johnston { 866*cef5f43fSMark Johnston struct make_dev_args mda; 867d5819709SMark Johnston struct cdev *cdev; 868*cef5f43fSMark Johnston struct vmmdev_softc *sc; 869d5819709SMark Johnston struct vm *vm; 870d5819709SMark Johnston int error; 871b9ef152bSMark Johnston 872887c0877SMark Johnston sx_xlock(&vmmdev_mtx); 873c23da668SMark Johnston sc = vmmdev_lookup(name, cred); 874*cef5f43fSMark Johnston if (sc != NULL) { 875887c0877SMark Johnston sx_xunlock(&vmmdev_mtx); 876d5819709SMark Johnston return (EEXIST); 877*cef5f43fSMark Johnston } 878d5819709SMark Johnston 879d5819709SMark Johnston error = vm_create(name, &vm); 880b9ef152bSMark Johnston if (error != 0) { 881*cef5f43fSMark Johnston sx_xunlock(&vmmdev_mtx); 882*cef5f43fSMark Johnston return (error); 883*cef5f43fSMark Johnston } 884*cef5f43fSMark Johnston sc = vmmdev_alloc(vm, cred); 885*cef5f43fSMark Johnston SLIST_INSERT_HEAD(&head, sc, link); 886*cef5f43fSMark Johnston 887*cef5f43fSMark Johnston make_dev_args_init(&mda); 888*cef5f43fSMark Johnston mda.mda_devsw = &vmmdevsw; 889*cef5f43fSMark Johnston mda.mda_cr = sc->ucred; 890*cef5f43fSMark Johnston mda.mda_uid = UID_ROOT; 891*cef5f43fSMark Johnston mda.mda_gid = GID_WHEEL; 892*cef5f43fSMark Johnston mda.mda_mode = 0600; 893*cef5f43fSMark Johnston mda.mda_si_drv1 = sc; 894*cef5f43fSMark Johnston mda.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; 895*cef5f43fSMark Johnston error = make_dev_s(&mda, &cdev, "vmm/%s", name); 896*cef5f43fSMark Johnston if (error != 0) { 897*cef5f43fSMark Johnston sx_xunlock(&vmmdev_mtx); 898b9ef152bSMark Johnston vmmdev_destroy(sc); 899d5819709SMark Johnston return (error); 900b9ef152bSMark Johnston } 901b9ef152bSMark Johnston sc->cdev = cdev; 902887c0877SMark Johnston sx_xunlock(&vmmdev_mtx); 903d5819709SMark Johnston return (0); 904d5819709SMark Johnston } 905d5819709SMark Johnston 906d5819709SMark Johnston static int 907d5819709SMark Johnston sysctl_vmm_create(SYSCTL_HANDLER_ARGS) 908d5819709SMark Johnston { 909d5819709SMark Johnston char *buf; 910d5819709SMark Johnston int error, buflen; 911d5819709SMark Johnston 912d5819709SMark Johnston error = vmm_priv_check(req->td->td_ucred); 913d5819709SMark Johnston if (error != 0) 914d5819709SMark Johnston return (error); 915d5819709SMark Johnston 916d5819709SMark Johnston buflen = VM_MAX_NAMELEN + 1; 917d5819709SMark Johnston buf = malloc(buflen, M_VMMDEV, M_WAITOK | M_ZERO); 918d5819709SMark Johnston strlcpy(buf, "beavis", buflen); 919d5819709SMark Johnston error = sysctl_handle_string(oidp, buf, buflen, req); 920d5819709SMark Johnston if (error == 0 && req->newptr != NULL) 921d5819709SMark Johnston error = vmmdev_create(buf, req->td->td_ucred); 922b9ef152bSMark Johnston free(buf, M_VMMDEV); 923b9ef152bSMark Johnston return (error); 924b9ef152bSMark Johnston } 925b9ef152bSMark Johnston SYSCTL_PROC(_hw_vmm, OID_AUTO, create, 926b9ef152bSMark Johnston CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 927b9ef152bSMark Johnston NULL, 0, sysctl_vmm_create, "A", 928b9ef152bSMark Johnston NULL); 929b9ef152bSMark Johnston 930b9ef152bSMark Johnston void 931b9ef152bSMark Johnston vmmdev_init(void) 932b9ef152bSMark Johnston { 933b9ef152bSMark Johnston pr_allow_flag = prison_add_allow(NULL, "vmm", NULL, 934b9ef152bSMark Johnston "Allow use of vmm in a jail."); 935b9ef152bSMark Johnston } 936b9ef152bSMark Johnston 937b9ef152bSMark Johnston int 938b9ef152bSMark Johnston vmmdev_cleanup(void) 939b9ef152bSMark Johnston { 940b9ef152bSMark Johnston int error; 941b9ef152bSMark Johnston 942b9ef152bSMark Johnston if (SLIST_EMPTY(&head)) 943b9ef152bSMark Johnston error = 0; 944b9ef152bSMark Johnston else 945b9ef152bSMark Johnston error = EBUSY; 946b9ef152bSMark Johnston 947b9ef152bSMark Johnston return (error); 948b9ef152bSMark Johnston } 949b9ef152bSMark Johnston 950b9ef152bSMark Johnston static int 951b9ef152bSMark Johnston devmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t len, 952b9ef152bSMark Johnston struct vm_object **objp, int nprot) 953b9ef152bSMark Johnston { 954b9ef152bSMark Johnston struct devmem_softc *dsc; 955b9ef152bSMark Johnston vm_ooffset_t first, last; 956b9ef152bSMark Johnston size_t seglen; 957b9ef152bSMark Johnston int error; 958b9ef152bSMark Johnston bool sysmem; 959b9ef152bSMark Johnston 960b9ef152bSMark Johnston dsc = cdev->si_drv1; 961b9ef152bSMark Johnston if (dsc == NULL) { 962b9ef152bSMark Johnston /* 'cdev' has been created but is not ready for use */ 963b9ef152bSMark Johnston return (ENXIO); 964b9ef152bSMark Johnston } 965b9ef152bSMark Johnston 966b9ef152bSMark Johnston first = *offset; 967b9ef152bSMark Johnston last = *offset + len; 968b9ef152bSMark Johnston if ((nprot & PROT_EXEC) || first < 0 || first >= last) 969b9ef152bSMark Johnston return (EINVAL); 970b9ef152bSMark Johnston 971b9ef152bSMark Johnston vm_slock_memsegs(dsc->sc->vm); 972b9ef152bSMark Johnston 973b9ef152bSMark Johnston error = vm_get_memseg(dsc->sc->vm, dsc->segid, &seglen, &sysmem, objp); 974b9ef152bSMark Johnston KASSERT(error == 0 && !sysmem && *objp != NULL, 975b9ef152bSMark Johnston ("%s: invalid devmem segment %d", __func__, dsc->segid)); 976b9ef152bSMark Johnston 977b9ef152bSMark Johnston if (seglen >= last) 978b9ef152bSMark Johnston vm_object_reference(*objp); 979b9ef152bSMark Johnston else 980b9ef152bSMark Johnston error = EINVAL; 981b9ef152bSMark Johnston 982b9ef152bSMark Johnston vm_unlock_memsegs(dsc->sc->vm); 983b9ef152bSMark Johnston return (error); 984b9ef152bSMark Johnston } 985b9ef152bSMark Johnston 986b9ef152bSMark Johnston static struct cdevsw devmemsw = { 987b9ef152bSMark Johnston .d_name = "devmem", 988b9ef152bSMark Johnston .d_version = D_VERSION, 989b9ef152bSMark Johnston .d_mmap_single = devmem_mmap_single, 990b9ef152bSMark Johnston }; 991b9ef152bSMark Johnston 992b9ef152bSMark Johnston static int 993f4002135SMark Johnston devmem_create_cdev(struct vmmdev_softc *sc, int segid, char *devname) 994b9ef152bSMark Johnston { 995*cef5f43fSMark Johnston struct make_dev_args mda; 996b9ef152bSMark Johnston struct devmem_softc *dsc; 997b9ef152bSMark Johnston int error; 998b9ef152bSMark Johnston 999*cef5f43fSMark Johnston sx_xlock(&vmmdev_mtx); 1000b9ef152bSMark Johnston 1001b9ef152bSMark Johnston dsc = malloc(sizeof(struct devmem_softc), M_VMMDEV, M_WAITOK | M_ZERO); 1002b9ef152bSMark Johnston dsc->segid = segid; 1003b9ef152bSMark Johnston dsc->name = devname; 1004b9ef152bSMark Johnston dsc->sc = sc; 1005b9ef152bSMark Johnston SLIST_INSERT_HEAD(&sc->devmem, dsc, link); 1006*cef5f43fSMark Johnston 1007*cef5f43fSMark Johnston make_dev_args_init(&mda); 1008*cef5f43fSMark Johnston mda.mda_devsw = &devmemsw; 1009*cef5f43fSMark Johnston mda.mda_cr = sc->ucred; 1010*cef5f43fSMark Johnston mda.mda_uid = UID_ROOT; 1011*cef5f43fSMark Johnston mda.mda_gid = GID_WHEEL; 1012*cef5f43fSMark Johnston mda.mda_mode = 0600; 1013*cef5f43fSMark Johnston mda.mda_si_drv1 = dsc; 1014*cef5f43fSMark Johnston mda.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK; 1015*cef5f43fSMark Johnston error = make_dev_s(&mda, &dsc->cdev, "vmm.io/%s.%s", vm_name(sc->vm), 1016*cef5f43fSMark Johnston devname); 1017*cef5f43fSMark Johnston if (error != 0) { 1018*cef5f43fSMark Johnston SLIST_REMOVE(&sc->devmem, dsc, devmem_softc, link); 1019*cef5f43fSMark Johnston free(dsc->name, M_VMMDEV); 1020*cef5f43fSMark Johnston free(dsc, M_VMMDEV); 1021*cef5f43fSMark Johnston } 1022*cef5f43fSMark Johnston 1023887c0877SMark Johnston sx_xunlock(&vmmdev_mtx); 1024b9ef152bSMark Johnston 1025*cef5f43fSMark Johnston return (error); 1026b9ef152bSMark Johnston } 1027b9ef152bSMark Johnston 1028b9ef152bSMark Johnston static void 1029b9ef152bSMark Johnston devmem_destroy(void *arg) 1030b9ef152bSMark Johnston { 1031b9ef152bSMark Johnston struct devmem_softc *dsc = arg; 1032b9ef152bSMark Johnston 1033*cef5f43fSMark Johnston destroy_dev(dsc->cdev); 1034b9ef152bSMark Johnston dsc->cdev = NULL; 1035b9ef152bSMark Johnston dsc->sc = NULL; 1036b9ef152bSMark Johnston } 1037