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