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/mutex.h> 17b9ef152bSMark Johnston #include <sys/proc.h> 18b9ef152bSMark Johnston #include <sys/queue.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 #define VSC_LINKED 0x01 62b9ef152bSMark Johnston 63b9ef152bSMark Johnston static SLIST_HEAD(, vmmdev_softc) head; 64b9ef152bSMark Johnston 65b9ef152bSMark Johnston static unsigned pr_allow_flag; 66b9ef152bSMark Johnston static struct mtx vmmdev_mtx; 67b9ef152bSMark Johnston MTX_SYSINIT(vmmdev_mtx, &vmmdev_mtx, "vmm device mutex", MTX_DEF); 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 159b9ef152bSMark Johnston mtx_assert(&vmmdev_mtx, MA_OWNED); 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 198b9ef152bSMark Johnston prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ); 199b9ef152bSMark Johnston maxaddr = vmm_sysmem_maxaddr(sc->vm); 200b9ef152bSMark Johnston while (uio->uio_resid > 0 && error == 0) { 201b9ef152bSMark Johnston gpa = uio->uio_offset; 202b9ef152bSMark Johnston off = gpa & PAGE_MASK; 203b9ef152bSMark Johnston c = min(uio->uio_resid, PAGE_SIZE - off); 204b9ef152bSMark Johnston 205b9ef152bSMark Johnston /* 206b9ef152bSMark Johnston * The VM has a hole in its physical memory map. If we want to 207b9ef152bSMark Johnston * use 'dd' to inspect memory beyond the hole we need to 208b9ef152bSMark Johnston * provide bogus data for memory that lies in the hole. 209b9ef152bSMark Johnston * 210b9ef152bSMark Johnston * Since this device does not support lseek(2), dd(1) will 211b9ef152bSMark Johnston * read(2) blocks of data to simulate the lseek(2). 212b9ef152bSMark Johnston */ 213b9ef152bSMark Johnston hpa = vm_gpa_hold_global(sc->vm, gpa, c, prot, &cookie); 214b9ef152bSMark Johnston if (hpa == NULL) { 215b9ef152bSMark Johnston if (uio->uio_rw == UIO_READ && gpa < maxaddr) 216b9ef152bSMark Johnston error = uiomove(__DECONST(void *, zero_region), 217b9ef152bSMark Johnston c, uio); 218b9ef152bSMark Johnston else 219b9ef152bSMark Johnston error = EFAULT; 220b9ef152bSMark Johnston } else { 221b9ef152bSMark Johnston error = uiomove(hpa, c, uio); 222b9ef152bSMark Johnston vm_gpa_release(cookie); 223b9ef152bSMark Johnston } 224b9ef152bSMark Johnston } 225b9ef152bSMark Johnston vm_unlock_memsegs(sc->vm); 226b9ef152bSMark Johnston return (error); 227b9ef152bSMark Johnston } 228b9ef152bSMark Johnston 229b9ef152bSMark Johnston CTASSERT(sizeof(((struct vm_memseg *)0)->name) >= VM_MAX_SUFFIXLEN + 1); 230b9ef152bSMark Johnston 231b9ef152bSMark Johnston static int 232b9ef152bSMark Johnston get_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg, size_t len) 233b9ef152bSMark Johnston { 234b9ef152bSMark Johnston struct devmem_softc *dsc; 235b9ef152bSMark Johnston int error; 236b9ef152bSMark Johnston bool sysmem; 237b9ef152bSMark Johnston 238b9ef152bSMark Johnston error = vm_get_memseg(sc->vm, mseg->segid, &mseg->len, &sysmem, NULL); 239b9ef152bSMark Johnston if (error || mseg->len == 0) 240b9ef152bSMark Johnston return (error); 241b9ef152bSMark Johnston 242b9ef152bSMark Johnston if (!sysmem) { 243b9ef152bSMark Johnston SLIST_FOREACH(dsc, &sc->devmem, link) { 244b9ef152bSMark Johnston if (dsc->segid == mseg->segid) 245b9ef152bSMark Johnston break; 246b9ef152bSMark Johnston } 247b9ef152bSMark Johnston KASSERT(dsc != NULL, ("%s: devmem segment %d not found", 248b9ef152bSMark Johnston __func__, mseg->segid)); 249b9ef152bSMark Johnston error = copystr(dsc->name, mseg->name, len, NULL); 250b9ef152bSMark Johnston } else { 251b9ef152bSMark Johnston bzero(mseg->name, len); 252b9ef152bSMark Johnston } 253b9ef152bSMark Johnston 254b9ef152bSMark Johnston return (error); 255b9ef152bSMark Johnston } 256b9ef152bSMark Johnston 257b9ef152bSMark Johnston static int 258b9ef152bSMark Johnston alloc_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg, size_t len) 259b9ef152bSMark Johnston { 260b9ef152bSMark Johnston char *name; 261b9ef152bSMark Johnston int error; 262b9ef152bSMark Johnston bool sysmem; 263b9ef152bSMark Johnston 264b9ef152bSMark Johnston error = 0; 265b9ef152bSMark Johnston name = NULL; 266b9ef152bSMark Johnston sysmem = true; 267b9ef152bSMark Johnston 268b9ef152bSMark Johnston /* 269b9ef152bSMark Johnston * The allocation is lengthened by 1 to hold a terminating NUL. It'll 270b9ef152bSMark Johnston * by stripped off when devfs processes the full string. 271b9ef152bSMark Johnston */ 272b9ef152bSMark Johnston if (VM_MEMSEG_NAME(mseg)) { 273b9ef152bSMark Johnston sysmem = false; 274b9ef152bSMark Johnston name = malloc(len, M_VMMDEV, M_WAITOK); 275b9ef152bSMark Johnston error = copystr(mseg->name, name, len, NULL); 276b9ef152bSMark Johnston if (error) 277b9ef152bSMark Johnston goto done; 278b9ef152bSMark Johnston } 279b9ef152bSMark Johnston 280b9ef152bSMark Johnston error = vm_alloc_memseg(sc->vm, mseg->segid, mseg->len, sysmem); 281b9ef152bSMark Johnston if (error) 282b9ef152bSMark Johnston goto done; 283b9ef152bSMark Johnston 284b9ef152bSMark Johnston if (VM_MEMSEG_NAME(mseg)) { 285f4002135SMark Johnston error = devmem_create_cdev(sc, mseg->segid, name); 286b9ef152bSMark Johnston if (error) 287b9ef152bSMark Johnston vm_free_memseg(sc->vm, mseg->segid); 288b9ef152bSMark Johnston else 289b9ef152bSMark Johnston name = NULL; /* freed when 'cdev' is destroyed */ 290b9ef152bSMark Johnston } 291b9ef152bSMark Johnston done: 292b9ef152bSMark Johnston free(name, M_VMMDEV); 293b9ef152bSMark Johnston return (error); 294b9ef152bSMark Johnston } 295b9ef152bSMark Johnston 296b9ef152bSMark Johnston static int 297b9ef152bSMark Johnston vm_get_register_set(struct vcpu *vcpu, unsigned int count, int *regnum, 298b9ef152bSMark Johnston uint64_t *regval) 299b9ef152bSMark Johnston { 300b9ef152bSMark Johnston int error, i; 301b9ef152bSMark Johnston 302b9ef152bSMark Johnston error = 0; 303b9ef152bSMark Johnston for (i = 0; i < count; i++) { 304b9ef152bSMark Johnston error = vm_get_register(vcpu, regnum[i], ®val[i]); 305b9ef152bSMark Johnston if (error) 306b9ef152bSMark Johnston break; 307b9ef152bSMark Johnston } 308b9ef152bSMark Johnston return (error); 309b9ef152bSMark Johnston } 310b9ef152bSMark Johnston 311b9ef152bSMark Johnston static int 312b9ef152bSMark Johnston vm_set_register_set(struct vcpu *vcpu, unsigned int count, int *regnum, 313b9ef152bSMark Johnston uint64_t *regval) 314b9ef152bSMark Johnston { 315b9ef152bSMark Johnston int error, i; 316b9ef152bSMark Johnston 317b9ef152bSMark Johnston error = 0; 318b9ef152bSMark Johnston for (i = 0; i < count; i++) { 319b9ef152bSMark Johnston error = vm_set_register(vcpu, regnum[i], regval[i]); 320b9ef152bSMark Johnston if (error) 321b9ef152bSMark Johnston break; 322b9ef152bSMark Johnston } 323b9ef152bSMark Johnston return (error); 324b9ef152bSMark Johnston } 325b9ef152bSMark Johnston 326*40087581SMark Johnston static int 327*40087581SMark Johnston vmmdev_open(struct cdev *dev, int flags, int fmt, struct thread *td) 328*40087581SMark Johnston { 329*40087581SMark Johnston struct vmmdev_softc *sc; 330*40087581SMark Johnston int error; 331*40087581SMark Johnston 332*40087581SMark Johnston sc = vmmdev_lookup2(dev); 333*40087581SMark Johnston KASSERT(sc != NULL, ("%s: device not found", __func__)); 334*40087581SMark Johnston 335*40087581SMark Johnston /* 336*40087581SMark Johnston * A user can only access VMs that they themselves have created. 337*40087581SMark Johnston */ 338*40087581SMark Johnston if (td->td_ucred != sc->ucred) 339*40087581SMark Johnston return (EPERM); 340*40087581SMark Johnston 341*40087581SMark Johnston /* 342*40087581SMark Johnston * A jail without vmm access shouldn't be able to access vmm device 343*40087581SMark Johnston * files at all, but check here just to be thorough. 344*40087581SMark Johnston */ 345*40087581SMark Johnston error = vmm_priv_check(td->td_ucred); 346*40087581SMark Johnston if (error != 0) 347*40087581SMark Johnston return (error); 348*40087581SMark Johnston 349*40087581SMark Johnston return (0); 350*40087581SMark Johnston } 351*40087581SMark Johnston 352b9ef152bSMark Johnston static const struct vmmdev_ioctl vmmdev_ioctls[] = { 353b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_REGISTER, VMMDEV_IOCTL_LOCK_ONE_VCPU), 354b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SET_REGISTER, VMMDEV_IOCTL_LOCK_ONE_VCPU), 355b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_REGISTER_SET, VMMDEV_IOCTL_LOCK_ONE_VCPU), 356b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SET_REGISTER_SET, VMMDEV_IOCTL_LOCK_ONE_VCPU), 357b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_CAPABILITY, VMMDEV_IOCTL_LOCK_ONE_VCPU), 358b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SET_CAPABILITY, VMMDEV_IOCTL_LOCK_ONE_VCPU), 359b9ef152bSMark Johnston VMMDEV_IOCTL(VM_ACTIVATE_CPU, VMMDEV_IOCTL_LOCK_ONE_VCPU), 360b9ef152bSMark Johnston VMMDEV_IOCTL(VM_INJECT_EXCEPTION, VMMDEV_IOCTL_LOCK_ONE_VCPU), 361b9ef152bSMark Johnston VMMDEV_IOCTL(VM_STATS, VMMDEV_IOCTL_LOCK_ONE_VCPU), 362b9ef152bSMark Johnston 363b9ef152bSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12) 364a852dc58SMark Johnston VMMDEV_IOCTL(VM_ALLOC_MEMSEG_12, 365b9ef152bSMark Johnston VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS), 366b9ef152bSMark Johnston #endif 367b9ef152bSMark Johnston VMMDEV_IOCTL(VM_ALLOC_MEMSEG, 368b9ef152bSMark Johnston VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS), 369b9ef152bSMark Johnston VMMDEV_IOCTL(VM_MMAP_MEMSEG, 370b9ef152bSMark Johnston VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS), 371b9ef152bSMark Johnston VMMDEV_IOCTL(VM_MUNMAP_MEMSEG, 372b9ef152bSMark Johnston VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS), 373b9ef152bSMark Johnston VMMDEV_IOCTL(VM_REINIT, 374b9ef152bSMark Johnston VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_LOCK_ALL_VCPUS), 375b9ef152bSMark Johnston 376b9ef152bSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12) 377a852dc58SMark Johnston VMMDEV_IOCTL(VM_GET_MEMSEG_12, VMMDEV_IOCTL_SLOCK_MEMSEGS), 378b9ef152bSMark Johnston #endif 379b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_MEMSEG, VMMDEV_IOCTL_SLOCK_MEMSEGS), 380b9ef152bSMark Johnston VMMDEV_IOCTL(VM_MMAP_GETNEXT, VMMDEV_IOCTL_SLOCK_MEMSEGS), 381b9ef152bSMark Johnston 382b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SUSPEND_CPU, VMMDEV_IOCTL_MAYBE_ALLOC_VCPU), 383b9ef152bSMark Johnston VMMDEV_IOCTL(VM_RESUME_CPU, VMMDEV_IOCTL_MAYBE_ALLOC_VCPU), 384b9ef152bSMark Johnston 385b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SUSPEND, 0), 386b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_CPUS, 0), 387b9ef152bSMark Johnston VMMDEV_IOCTL(VM_GET_TOPOLOGY, 0), 388b9ef152bSMark Johnston VMMDEV_IOCTL(VM_SET_TOPOLOGY, 0), 389b9ef152bSMark Johnston }; 390b9ef152bSMark Johnston 391b9ef152bSMark Johnston static int 392b9ef152bSMark Johnston vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 393b9ef152bSMark Johnston struct thread *td) 394b9ef152bSMark Johnston { 395b9ef152bSMark Johnston struct vmmdev_softc *sc; 396b9ef152bSMark Johnston struct vcpu *vcpu; 397b9ef152bSMark Johnston const struct vmmdev_ioctl *ioctl; 398b9ef152bSMark Johnston int error, vcpuid; 399b9ef152bSMark Johnston 400b9ef152bSMark Johnston sc = vmmdev_lookup2(cdev); 401b9ef152bSMark Johnston if (sc == NULL) 402b9ef152bSMark Johnston return (ENXIO); 403b9ef152bSMark Johnston 404b9ef152bSMark Johnston ioctl = NULL; 405b9ef152bSMark Johnston for (size_t i = 0; i < nitems(vmmdev_ioctls); i++) { 406b9ef152bSMark Johnston if (vmmdev_ioctls[i].cmd == cmd) { 407b9ef152bSMark Johnston ioctl = &vmmdev_ioctls[i]; 408b9ef152bSMark Johnston break; 409b9ef152bSMark Johnston } 410b9ef152bSMark Johnston } 411b9ef152bSMark Johnston if (ioctl == NULL) { 412b9ef152bSMark Johnston for (size_t i = 0; i < vmmdev_machdep_ioctl_count; i++) { 413b9ef152bSMark Johnston if (vmmdev_machdep_ioctls[i].cmd == cmd) { 414b9ef152bSMark Johnston ioctl = &vmmdev_machdep_ioctls[i]; 415b9ef152bSMark Johnston break; 416b9ef152bSMark Johnston } 417b9ef152bSMark Johnston } 418b9ef152bSMark Johnston } 419b9ef152bSMark Johnston if (ioctl == NULL) 420b9ef152bSMark Johnston return (ENOTTY); 421b9ef152bSMark Johnston 422b9ef152bSMark Johnston if ((ioctl->flags & VMMDEV_IOCTL_XLOCK_MEMSEGS) != 0) 423b9ef152bSMark Johnston vm_xlock_memsegs(sc->vm); 424b9ef152bSMark Johnston else if ((ioctl->flags & VMMDEV_IOCTL_SLOCK_MEMSEGS) != 0) 425b9ef152bSMark Johnston vm_slock_memsegs(sc->vm); 426b9ef152bSMark Johnston 427b9ef152bSMark Johnston vcpu = NULL; 428b9ef152bSMark Johnston vcpuid = -1; 429b9ef152bSMark Johnston if ((ioctl->flags & (VMMDEV_IOCTL_LOCK_ONE_VCPU | 430b9ef152bSMark Johnston VMMDEV_IOCTL_ALLOC_VCPU | VMMDEV_IOCTL_MAYBE_ALLOC_VCPU)) != 0) { 431b9ef152bSMark Johnston vcpuid = *(int *)data; 432b9ef152bSMark Johnston if (vcpuid == -1) { 433b9ef152bSMark Johnston if ((ioctl->flags & 434b9ef152bSMark Johnston VMMDEV_IOCTL_MAYBE_ALLOC_VCPU) == 0) { 435b9ef152bSMark Johnston error = EINVAL; 436b9ef152bSMark Johnston goto lockfail; 437b9ef152bSMark Johnston } 438b9ef152bSMark Johnston } else { 439b9ef152bSMark Johnston vcpu = vm_alloc_vcpu(sc->vm, vcpuid); 440b9ef152bSMark Johnston if (vcpu == NULL) { 441b9ef152bSMark Johnston error = EINVAL; 442b9ef152bSMark Johnston goto lockfail; 443b9ef152bSMark Johnston } 444b9ef152bSMark Johnston if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ONE_VCPU) != 0) { 445b9ef152bSMark Johnston error = vcpu_lock_one(vcpu); 446b9ef152bSMark Johnston if (error) 447b9ef152bSMark Johnston goto lockfail; 448b9ef152bSMark Johnston } 449b9ef152bSMark Johnston } 450b9ef152bSMark Johnston } 451b9ef152bSMark Johnston if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ALL_VCPUS) != 0) { 452b9ef152bSMark Johnston error = vcpu_lock_all(sc); 453b9ef152bSMark Johnston if (error) 454b9ef152bSMark Johnston goto lockfail; 455b9ef152bSMark Johnston } 456b9ef152bSMark Johnston 457b9ef152bSMark Johnston switch (cmd) { 458b9ef152bSMark Johnston case VM_SUSPEND: { 459b9ef152bSMark Johnston struct vm_suspend *vmsuspend; 460b9ef152bSMark Johnston 461b9ef152bSMark Johnston vmsuspend = (struct vm_suspend *)data; 462b9ef152bSMark Johnston error = vm_suspend(sc->vm, vmsuspend->how); 463b9ef152bSMark Johnston break; 464b9ef152bSMark Johnston } 465b9ef152bSMark Johnston case VM_REINIT: 466b9ef152bSMark Johnston error = vm_reinit(sc->vm); 467b9ef152bSMark Johnston break; 468b9ef152bSMark Johnston case VM_STAT_DESC: { 469b9ef152bSMark Johnston struct vm_stat_desc *statdesc; 470b9ef152bSMark Johnston 471b9ef152bSMark Johnston statdesc = (struct vm_stat_desc *)data; 472b9ef152bSMark Johnston error = vmm_stat_desc_copy(statdesc->index, statdesc->desc, 473b9ef152bSMark Johnston sizeof(statdesc->desc)); 474b9ef152bSMark Johnston break; 475b9ef152bSMark Johnston } 476b9ef152bSMark Johnston case VM_STATS: { 477b9ef152bSMark Johnston struct vm_stats *vmstats; 478b9ef152bSMark Johnston 479b9ef152bSMark Johnston vmstats = (struct vm_stats *)data; 480b9ef152bSMark Johnston getmicrotime(&vmstats->tv); 481b9ef152bSMark Johnston error = vmm_stat_copy(vcpu, vmstats->index, 482b9ef152bSMark Johnston nitems(vmstats->statbuf), &vmstats->num_entries, 483b9ef152bSMark Johnston vmstats->statbuf); 484b9ef152bSMark Johnston break; 485b9ef152bSMark Johnston } 486b9ef152bSMark Johnston case VM_MMAP_GETNEXT: { 487b9ef152bSMark Johnston struct vm_memmap *mm; 488b9ef152bSMark Johnston 489b9ef152bSMark Johnston mm = (struct vm_memmap *)data; 490b9ef152bSMark Johnston error = vm_mmap_getnext(sc->vm, &mm->gpa, &mm->segid, 491b9ef152bSMark Johnston &mm->segoff, &mm->len, &mm->prot, &mm->flags); 492b9ef152bSMark Johnston break; 493b9ef152bSMark Johnston } 494b9ef152bSMark Johnston case VM_MMAP_MEMSEG: { 495b9ef152bSMark Johnston struct vm_memmap *mm; 496b9ef152bSMark Johnston 497b9ef152bSMark Johnston mm = (struct vm_memmap *)data; 498b9ef152bSMark Johnston error = vm_mmap_memseg(sc->vm, mm->gpa, mm->segid, mm->segoff, 499b9ef152bSMark Johnston mm->len, mm->prot, mm->flags); 500b9ef152bSMark Johnston break; 501b9ef152bSMark Johnston } 502b9ef152bSMark Johnston case VM_MUNMAP_MEMSEG: { 503b9ef152bSMark Johnston struct vm_munmap *mu; 504b9ef152bSMark Johnston 505b9ef152bSMark Johnston mu = (struct vm_munmap *)data; 506b9ef152bSMark Johnston error = vm_munmap_memseg(sc->vm, mu->gpa, mu->len); 507b9ef152bSMark Johnston break; 508b9ef152bSMark Johnston } 509b9ef152bSMark Johnston #if defined(__amd64__) && defined(COMPAT_FREEBSD12) 510a852dc58SMark Johnston case VM_ALLOC_MEMSEG_12: 511b9ef152bSMark Johnston error = alloc_memseg(sc, (struct vm_memseg *)data, 512a852dc58SMark Johnston sizeof(((struct vm_memseg_12 *)0)->name)); 513b9ef152bSMark Johnston break; 514a852dc58SMark Johnston case VM_GET_MEMSEG_12: 515b9ef152bSMark Johnston error = get_memseg(sc, (struct vm_memseg *)data, 516a852dc58SMark Johnston sizeof(((struct vm_memseg_12 *)0)->name)); 517b9ef152bSMark Johnston break; 518b9ef152bSMark Johnston #endif 519b9ef152bSMark Johnston case VM_ALLOC_MEMSEG: 520b9ef152bSMark Johnston error = alloc_memseg(sc, (struct vm_memseg *)data, 521b9ef152bSMark Johnston sizeof(((struct vm_memseg *)0)->name)); 522b9ef152bSMark Johnston break; 523b9ef152bSMark Johnston case VM_GET_MEMSEG: 524b9ef152bSMark Johnston error = get_memseg(sc, (struct vm_memseg *)data, 525b9ef152bSMark Johnston sizeof(((struct vm_memseg *)0)->name)); 526b9ef152bSMark Johnston break; 527b9ef152bSMark Johnston case VM_GET_REGISTER: { 528b9ef152bSMark Johnston struct vm_register *vmreg; 529b9ef152bSMark Johnston 530b9ef152bSMark Johnston vmreg = (struct vm_register *)data; 531b9ef152bSMark Johnston error = vm_get_register(vcpu, vmreg->regnum, &vmreg->regval); 532b9ef152bSMark Johnston break; 533b9ef152bSMark Johnston } 534b9ef152bSMark Johnston case VM_SET_REGISTER: { 535b9ef152bSMark Johnston struct vm_register *vmreg; 536b9ef152bSMark Johnston 537b9ef152bSMark Johnston vmreg = (struct vm_register *)data; 538b9ef152bSMark Johnston error = vm_set_register(vcpu, vmreg->regnum, vmreg->regval); 539b9ef152bSMark Johnston break; 540b9ef152bSMark Johnston } 541b9ef152bSMark Johnston case VM_GET_REGISTER_SET: { 542b9ef152bSMark Johnston struct vm_register_set *vmregset; 543b9ef152bSMark Johnston uint64_t *regvals; 544b9ef152bSMark Johnston int *regnums; 545b9ef152bSMark Johnston 546b9ef152bSMark Johnston vmregset = (struct vm_register_set *)data; 547b9ef152bSMark Johnston if (vmregset->count > VM_REG_LAST) { 548b9ef152bSMark Johnston error = EINVAL; 549b9ef152bSMark Johnston break; 550b9ef152bSMark Johnston } 551b9ef152bSMark Johnston regvals = malloc(sizeof(regvals[0]) * vmregset->count, M_VMMDEV, 552b9ef152bSMark Johnston M_WAITOK); 553b9ef152bSMark Johnston regnums = malloc(sizeof(regnums[0]) * vmregset->count, M_VMMDEV, 554b9ef152bSMark Johnston M_WAITOK); 555b9ef152bSMark Johnston error = copyin(vmregset->regnums, regnums, sizeof(regnums[0]) * 556b9ef152bSMark Johnston vmregset->count); 557b9ef152bSMark Johnston if (error == 0) 558b9ef152bSMark Johnston error = vm_get_register_set(vcpu, 559b9ef152bSMark Johnston vmregset->count, regnums, regvals); 560b9ef152bSMark Johnston if (error == 0) 561b9ef152bSMark Johnston error = copyout(regvals, vmregset->regvals, 562b9ef152bSMark Johnston sizeof(regvals[0]) * vmregset->count); 563b9ef152bSMark Johnston free(regvals, M_VMMDEV); 564b9ef152bSMark Johnston free(regnums, M_VMMDEV); 565b9ef152bSMark Johnston break; 566b9ef152bSMark Johnston } 567b9ef152bSMark Johnston case VM_SET_REGISTER_SET: { 568b9ef152bSMark Johnston struct vm_register_set *vmregset; 569b9ef152bSMark Johnston uint64_t *regvals; 570b9ef152bSMark Johnston int *regnums; 571b9ef152bSMark Johnston 572b9ef152bSMark Johnston vmregset = (struct vm_register_set *)data; 573b9ef152bSMark Johnston if (vmregset->count > VM_REG_LAST) { 574b9ef152bSMark Johnston error = EINVAL; 575b9ef152bSMark Johnston break; 576b9ef152bSMark Johnston } 577b9ef152bSMark Johnston regvals = malloc(sizeof(regvals[0]) * vmregset->count, M_VMMDEV, 578b9ef152bSMark Johnston M_WAITOK); 579b9ef152bSMark Johnston regnums = malloc(sizeof(regnums[0]) * vmregset->count, M_VMMDEV, 580b9ef152bSMark Johnston M_WAITOK); 581b9ef152bSMark Johnston error = copyin(vmregset->regnums, regnums, sizeof(regnums[0]) * 582b9ef152bSMark Johnston vmregset->count); 583b9ef152bSMark Johnston if (error == 0) 584b9ef152bSMark Johnston error = copyin(vmregset->regvals, regvals, 585b9ef152bSMark Johnston sizeof(regvals[0]) * vmregset->count); 586b9ef152bSMark Johnston if (error == 0) 587b9ef152bSMark Johnston error = vm_set_register_set(vcpu, 588b9ef152bSMark Johnston vmregset->count, regnums, regvals); 589b9ef152bSMark Johnston free(regvals, M_VMMDEV); 590b9ef152bSMark Johnston free(regnums, M_VMMDEV); 591b9ef152bSMark Johnston break; 592b9ef152bSMark Johnston } 593b9ef152bSMark Johnston case VM_GET_CAPABILITY: { 594b9ef152bSMark Johnston struct vm_capability *vmcap; 595b9ef152bSMark Johnston 596b9ef152bSMark Johnston vmcap = (struct vm_capability *)data; 597b9ef152bSMark Johnston error = vm_get_capability(vcpu, vmcap->captype, &vmcap->capval); 598b9ef152bSMark Johnston break; 599b9ef152bSMark Johnston } 600b9ef152bSMark Johnston case VM_SET_CAPABILITY: { 601b9ef152bSMark Johnston struct vm_capability *vmcap; 602b9ef152bSMark Johnston 603b9ef152bSMark Johnston vmcap = (struct vm_capability *)data; 604b9ef152bSMark Johnston error = vm_set_capability(vcpu, vmcap->captype, vmcap->capval); 605b9ef152bSMark Johnston break; 606b9ef152bSMark Johnston } 607b9ef152bSMark Johnston case VM_ACTIVATE_CPU: 608b9ef152bSMark Johnston error = vm_activate_cpu(vcpu); 609b9ef152bSMark Johnston break; 610b9ef152bSMark Johnston case VM_GET_CPUS: { 611b9ef152bSMark Johnston struct vm_cpuset *vm_cpuset; 612b9ef152bSMark Johnston cpuset_t *cpuset; 613b9ef152bSMark Johnston int size; 614b9ef152bSMark Johnston 615b9ef152bSMark Johnston error = 0; 616b9ef152bSMark Johnston vm_cpuset = (struct vm_cpuset *)data; 617b9ef152bSMark Johnston size = vm_cpuset->cpusetsize; 618b9ef152bSMark Johnston if (size < 1 || size > CPU_MAXSIZE / NBBY) { 619b9ef152bSMark Johnston error = ERANGE; 620b9ef152bSMark Johnston break; 621b9ef152bSMark Johnston } 622b9ef152bSMark Johnston cpuset = malloc(max(size, sizeof(cpuset_t)), M_TEMP, 623b9ef152bSMark Johnston M_WAITOK | M_ZERO); 624b9ef152bSMark Johnston if (vm_cpuset->which == VM_ACTIVE_CPUS) 625b9ef152bSMark Johnston *cpuset = vm_active_cpus(sc->vm); 626b9ef152bSMark Johnston else if (vm_cpuset->which == VM_SUSPENDED_CPUS) 627b9ef152bSMark Johnston *cpuset = vm_suspended_cpus(sc->vm); 628b9ef152bSMark Johnston else if (vm_cpuset->which == VM_DEBUG_CPUS) 629b9ef152bSMark Johnston *cpuset = vm_debug_cpus(sc->vm); 630b9ef152bSMark Johnston else 631b9ef152bSMark Johnston error = EINVAL; 632b9ef152bSMark Johnston if (error == 0 && size < howmany(CPU_FLS(cpuset), NBBY)) 633b9ef152bSMark Johnston error = ERANGE; 634b9ef152bSMark Johnston if (error == 0) 635b9ef152bSMark Johnston error = copyout(cpuset, vm_cpuset->cpus, size); 636b9ef152bSMark Johnston free(cpuset, M_TEMP); 637b9ef152bSMark Johnston break; 638b9ef152bSMark Johnston } 639b9ef152bSMark Johnston case VM_SUSPEND_CPU: 640b9ef152bSMark Johnston error = vm_suspend_cpu(sc->vm, vcpu); 641b9ef152bSMark Johnston break; 642b9ef152bSMark Johnston case VM_RESUME_CPU: 643b9ef152bSMark Johnston error = vm_resume_cpu(sc->vm, vcpu); 644b9ef152bSMark Johnston break; 645b9ef152bSMark Johnston case VM_SET_TOPOLOGY: { 646b9ef152bSMark Johnston struct vm_cpu_topology *topology; 647b9ef152bSMark Johnston 648b9ef152bSMark Johnston topology = (struct vm_cpu_topology *)data; 649b9ef152bSMark Johnston error = vm_set_topology(sc->vm, topology->sockets, 650b9ef152bSMark Johnston topology->cores, topology->threads, topology->maxcpus); 651b9ef152bSMark Johnston break; 652b9ef152bSMark Johnston } 653b9ef152bSMark Johnston case VM_GET_TOPOLOGY: { 654b9ef152bSMark Johnston struct vm_cpu_topology *topology; 655b9ef152bSMark Johnston 656b9ef152bSMark Johnston topology = (struct vm_cpu_topology *)data; 657b9ef152bSMark Johnston vm_get_topology(sc->vm, &topology->sockets, &topology->cores, 658b9ef152bSMark Johnston &topology->threads, &topology->maxcpus); 659b9ef152bSMark Johnston error = 0; 660b9ef152bSMark Johnston break; 661b9ef152bSMark Johnston } 662b9ef152bSMark Johnston default: 663b9ef152bSMark Johnston error = vmmdev_machdep_ioctl(sc->vm, vcpu, cmd, data, fflag, 664b9ef152bSMark Johnston td); 665b9ef152bSMark Johnston break; 666b9ef152bSMark Johnston } 667b9ef152bSMark Johnston 668b9ef152bSMark Johnston if ((ioctl->flags & 669b9ef152bSMark Johnston (VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_SLOCK_MEMSEGS)) != 0) 670b9ef152bSMark Johnston vm_unlock_memsegs(sc->vm); 671b9ef152bSMark Johnston if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ALL_VCPUS) != 0) 672b9ef152bSMark Johnston vcpu_unlock_all(sc); 673b9ef152bSMark Johnston else if ((ioctl->flags & VMMDEV_IOCTL_LOCK_ONE_VCPU) != 0) 674b9ef152bSMark Johnston vcpu_unlock_one(vcpu); 675b9ef152bSMark Johnston 676b9ef152bSMark Johnston /* 677b9ef152bSMark Johnston * Make sure that no handler returns a kernel-internal 678b9ef152bSMark Johnston * error value to userspace. 679b9ef152bSMark Johnston */ 680b9ef152bSMark Johnston KASSERT(error == ERESTART || error >= 0, 681b9ef152bSMark Johnston ("vmmdev_ioctl: invalid error return %d", error)); 682b9ef152bSMark Johnston return (error); 683b9ef152bSMark Johnston 684b9ef152bSMark Johnston lockfail: 685b9ef152bSMark Johnston if ((ioctl->flags & 686b9ef152bSMark Johnston (VMMDEV_IOCTL_XLOCK_MEMSEGS | VMMDEV_IOCTL_SLOCK_MEMSEGS)) != 0) 687b9ef152bSMark Johnston vm_unlock_memsegs(sc->vm); 688b9ef152bSMark Johnston return (error); 689b9ef152bSMark Johnston } 690b9ef152bSMark Johnston 691b9ef152bSMark Johnston static int 692b9ef152bSMark Johnston vmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t mapsize, 693b9ef152bSMark Johnston struct vm_object **objp, int nprot) 694b9ef152bSMark Johnston { 695b9ef152bSMark Johnston struct vmmdev_softc *sc; 696b9ef152bSMark Johnston vm_paddr_t gpa; 697b9ef152bSMark Johnston size_t len; 698b9ef152bSMark Johnston vm_ooffset_t segoff, first, last; 699b9ef152bSMark Johnston int error, found, segid; 700b9ef152bSMark Johnston bool sysmem; 701b9ef152bSMark Johnston 702b9ef152bSMark Johnston first = *offset; 703b9ef152bSMark Johnston last = first + mapsize; 704b9ef152bSMark Johnston if ((nprot & PROT_EXEC) || first < 0 || first >= last) 705b9ef152bSMark Johnston return (EINVAL); 706b9ef152bSMark Johnston 707b9ef152bSMark Johnston sc = vmmdev_lookup2(cdev); 708b9ef152bSMark Johnston if (sc == NULL) { 709b9ef152bSMark Johnston /* virtual machine is in the process of being created */ 710b9ef152bSMark Johnston return (EINVAL); 711b9ef152bSMark Johnston } 712b9ef152bSMark Johnston 713b9ef152bSMark Johnston /* 714b9ef152bSMark Johnston * Get a read lock on the guest memory map. 715b9ef152bSMark Johnston */ 716b9ef152bSMark Johnston vm_slock_memsegs(sc->vm); 717b9ef152bSMark Johnston 718b9ef152bSMark Johnston gpa = 0; 719b9ef152bSMark Johnston found = 0; 720b9ef152bSMark Johnston while (!found) { 721b9ef152bSMark Johnston error = vm_mmap_getnext(sc->vm, &gpa, &segid, &segoff, &len, 722b9ef152bSMark Johnston NULL, NULL); 723b9ef152bSMark Johnston if (error) 724b9ef152bSMark Johnston break; 725b9ef152bSMark Johnston 726b9ef152bSMark Johnston if (first >= gpa && last <= gpa + len) 727b9ef152bSMark Johnston found = 1; 728b9ef152bSMark Johnston else 729b9ef152bSMark Johnston gpa += len; 730b9ef152bSMark Johnston } 731b9ef152bSMark Johnston 732b9ef152bSMark Johnston if (found) { 733b9ef152bSMark Johnston error = vm_get_memseg(sc->vm, segid, &len, &sysmem, objp); 734b9ef152bSMark Johnston KASSERT(error == 0 && *objp != NULL, 735b9ef152bSMark Johnston ("%s: invalid memory segment %d", __func__, segid)); 736b9ef152bSMark Johnston if (sysmem) { 737b9ef152bSMark Johnston vm_object_reference(*objp); 738b9ef152bSMark Johnston *offset = segoff + (first - gpa); 739b9ef152bSMark Johnston } else { 740b9ef152bSMark Johnston error = EINVAL; 741b9ef152bSMark Johnston } 742b9ef152bSMark Johnston } 743b9ef152bSMark Johnston vm_unlock_memsegs(sc->vm); 744b9ef152bSMark Johnston return (error); 745b9ef152bSMark Johnston } 746b9ef152bSMark Johnston 747b9ef152bSMark Johnston static void 748063a8bd9SMark Johnston vmmdev_destroy(struct vmmdev_softc *sc) 749b9ef152bSMark Johnston { 750b9ef152bSMark Johnston struct devmem_softc *dsc; 751b9ef152bSMark Johnston int error __diagused; 752b9ef152bSMark Johnston 753063a8bd9SMark Johnston /* 754063a8bd9SMark Johnston * Destroy all cdevs: 755063a8bd9SMark Johnston * 756063a8bd9SMark Johnston * - any new operations on the 'cdev' will return an error (ENXIO). 757063a8bd9SMark Johnston * 758063a8bd9SMark Johnston * - the 'devmem' cdevs are destroyed before the virtual machine 'cdev' 759063a8bd9SMark Johnston */ 760063a8bd9SMark Johnston SLIST_FOREACH(dsc, &sc->devmem, link) { 761063a8bd9SMark Johnston KASSERT(dsc->cdev != NULL, ("devmem cdev already destroyed")); 762063a8bd9SMark Johnston destroy_dev(dsc->cdev); 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->cdev != NULL) 779b9ef152bSMark Johnston destroy_dev(sc->cdev); 780b9ef152bSMark Johnston 781b9ef152bSMark Johnston if (sc->vm != NULL) 782b9ef152bSMark Johnston vm_destroy(sc->vm); 783b9ef152bSMark Johnston 784b9ef152bSMark Johnston if (sc->ucred != NULL) 785b9ef152bSMark Johnston crfree(sc->ucred); 786b9ef152bSMark Johnston 787b9ef152bSMark Johnston if ((sc->flags & VSC_LINKED) != 0) { 788b9ef152bSMark Johnston mtx_lock(&vmmdev_mtx); 789b9ef152bSMark Johnston SLIST_REMOVE(&head, sc, vmmdev_softc, link); 790b9ef152bSMark Johnston mtx_unlock(&vmmdev_mtx); 791b9ef152bSMark Johnston } 792b9ef152bSMark Johnston 793b9ef152bSMark Johnston free(sc, M_VMMDEV); 794b9ef152bSMark Johnston } 795b9ef152bSMark Johnston 796b9ef152bSMark Johnston static int 797063a8bd9SMark Johnston vmmdev_lookup_and_destroy(const char *name, struct ucred *cred) 798063a8bd9SMark Johnston { 799063a8bd9SMark Johnston struct cdev *cdev; 800063a8bd9SMark Johnston struct vmmdev_softc *sc; 801063a8bd9SMark Johnston 802063a8bd9SMark Johnston mtx_lock(&vmmdev_mtx); 803c23da668SMark Johnston sc = vmmdev_lookup(name, cred); 804063a8bd9SMark Johnston if (sc == NULL || sc->cdev == NULL) { 805063a8bd9SMark Johnston mtx_unlock(&vmmdev_mtx); 806063a8bd9SMark Johnston return (EINVAL); 807063a8bd9SMark Johnston } 808063a8bd9SMark Johnston 809063a8bd9SMark Johnston /* 810063a8bd9SMark Johnston * Setting 'sc->cdev' to NULL is used to indicate that the VM 811063a8bd9SMark Johnston * is scheduled for destruction. 812063a8bd9SMark Johnston */ 813063a8bd9SMark Johnston cdev = sc->cdev; 814063a8bd9SMark Johnston sc->cdev = NULL; 815063a8bd9SMark Johnston mtx_unlock(&vmmdev_mtx); 816063a8bd9SMark Johnston 817063a8bd9SMark Johnston destroy_dev(cdev); 818063a8bd9SMark Johnston vmmdev_destroy(sc); 819063a8bd9SMark Johnston 820063a8bd9SMark Johnston return (0); 821063a8bd9SMark Johnston } 822063a8bd9SMark Johnston 823063a8bd9SMark Johnston static int 824b9ef152bSMark Johnston sysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) 825b9ef152bSMark Johnston { 826b9ef152bSMark Johnston char *buf; 827b9ef152bSMark Johnston int error, buflen; 828b9ef152bSMark Johnston 829b9ef152bSMark Johnston error = vmm_priv_check(req->td->td_ucred); 830b9ef152bSMark Johnston if (error) 831b9ef152bSMark Johnston return (error); 832b9ef152bSMark Johnston 833b9ef152bSMark Johnston buflen = VM_MAX_NAMELEN + 1; 834b9ef152bSMark Johnston buf = malloc(buflen, M_VMMDEV, M_WAITOK | M_ZERO); 835b9ef152bSMark Johnston strlcpy(buf, "beavis", buflen); 836b9ef152bSMark Johnston error = sysctl_handle_string(oidp, buf, buflen, req); 837063a8bd9SMark Johnston if (error == 0 && req->newptr != NULL) 838063a8bd9SMark Johnston error = vmmdev_lookup_and_destroy(buf, req->td->td_ucred); 839b9ef152bSMark Johnston free(buf, M_VMMDEV); 840b9ef152bSMark Johnston return (error); 841b9ef152bSMark Johnston } 842b9ef152bSMark Johnston SYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, 843b9ef152bSMark Johnston CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 844b9ef152bSMark Johnston NULL, 0, sysctl_vmm_destroy, "A", 845b9ef152bSMark Johnston NULL); 846b9ef152bSMark Johnston 847b9ef152bSMark Johnston static struct cdevsw vmmdevsw = { 848b9ef152bSMark Johnston .d_name = "vmmdev", 849b9ef152bSMark Johnston .d_version = D_VERSION, 850*40087581SMark Johnston .d_open = vmmdev_open, 851b9ef152bSMark Johnston .d_ioctl = vmmdev_ioctl, 852b9ef152bSMark Johnston .d_mmap_single = vmmdev_mmap_single, 853b9ef152bSMark Johnston .d_read = vmmdev_rw, 854b9ef152bSMark Johnston .d_write = vmmdev_rw, 855b9ef152bSMark Johnston }; 856b9ef152bSMark Johnston 857d5819709SMark Johnston static struct vmmdev_softc * 858d5819709SMark Johnston vmmdev_alloc(struct vm *vm, struct ucred *cred) 859b9ef152bSMark Johnston { 860d5819709SMark Johnston struct vmmdev_softc *sc; 861b9ef152bSMark Johnston 862d5819709SMark Johnston sc = malloc(sizeof(*sc), M_VMMDEV, M_WAITOK | M_ZERO); 863d5819709SMark Johnston SLIST_INIT(&sc->devmem); 864d5819709SMark Johnston sc->vm = vm; 865d5819709SMark Johnston sc->ucred = crhold(cred); 866d5819709SMark Johnston return (sc); 867b9ef152bSMark Johnston } 868b9ef152bSMark Johnston 869d5819709SMark Johnston static int 870d5819709SMark Johnston vmmdev_create(const char *name, struct ucred *cred) 871d5819709SMark Johnston { 872d5819709SMark Johnston struct cdev *cdev; 873d5819709SMark Johnston struct vmmdev_softc *sc, *sc2; 874d5819709SMark Johnston struct vm *vm; 875d5819709SMark Johnston int error; 876b9ef152bSMark Johnston 877d5819709SMark Johnston mtx_lock(&vmmdev_mtx); 878c23da668SMark Johnston sc = vmmdev_lookup(name, cred); 879d5819709SMark Johnston mtx_unlock(&vmmdev_mtx); 880d5819709SMark Johnston if (sc != NULL) 881d5819709SMark Johnston return (EEXIST); 882d5819709SMark Johnston 883d5819709SMark Johnston error = vm_create(name, &vm); 884d5819709SMark Johnston if (error != 0) 885d5819709SMark Johnston return (error); 886d5819709SMark Johnston 887d5819709SMark Johnston sc = vmmdev_alloc(vm, cred); 888b9ef152bSMark Johnston 889b9ef152bSMark Johnston /* 890b9ef152bSMark Johnston * Lookup the name again just in case somebody sneaked in when we 891b9ef152bSMark Johnston * dropped the lock. 892b9ef152bSMark Johnston */ 893b9ef152bSMark Johnston mtx_lock(&vmmdev_mtx); 894c23da668SMark Johnston sc2 = vmmdev_lookup(name, cred); 895d5819709SMark Johnston if (sc2 != NULL) { 896d5819709SMark Johnston mtx_unlock(&vmmdev_mtx); 897d5819709SMark Johnston vmmdev_destroy(sc); 898d5819709SMark Johnston return (EEXIST); 899b9ef152bSMark Johnston } 900d5819709SMark Johnston sc->flags |= VSC_LINKED; 901d5819709SMark Johnston SLIST_INSERT_HEAD(&head, sc, link); 902b9ef152bSMark Johnston mtx_unlock(&vmmdev_mtx); 903b9ef152bSMark Johnston 904b9ef152bSMark Johnston error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, sc->ucred, 905d5819709SMark Johnston UID_ROOT, GID_WHEEL, 0600, "vmm/%s", name); 906b9ef152bSMark Johnston if (error != 0) { 907b9ef152bSMark Johnston vmmdev_destroy(sc); 908d5819709SMark Johnston return (error); 909b9ef152bSMark Johnston } 910b9ef152bSMark Johnston 911b9ef152bSMark Johnston mtx_lock(&vmmdev_mtx); 912b9ef152bSMark Johnston sc->cdev = cdev; 913b9ef152bSMark Johnston sc->cdev->si_drv1 = sc; 914b9ef152bSMark Johnston mtx_unlock(&vmmdev_mtx); 915b9ef152bSMark Johnston 916d5819709SMark Johnston return (0); 917d5819709SMark Johnston } 918d5819709SMark Johnston 919d5819709SMark Johnston static int 920d5819709SMark Johnston sysctl_vmm_create(SYSCTL_HANDLER_ARGS) 921d5819709SMark Johnston { 922d5819709SMark Johnston char *buf; 923d5819709SMark Johnston int error, buflen; 924d5819709SMark Johnston 925d5819709SMark Johnston error = vmm_priv_check(req->td->td_ucred); 926d5819709SMark Johnston if (error != 0) 927d5819709SMark Johnston return (error); 928d5819709SMark Johnston 929d5819709SMark Johnston buflen = VM_MAX_NAMELEN + 1; 930d5819709SMark Johnston buf = malloc(buflen, M_VMMDEV, M_WAITOK | M_ZERO); 931d5819709SMark Johnston strlcpy(buf, "beavis", buflen); 932d5819709SMark Johnston error = sysctl_handle_string(oidp, buf, buflen, req); 933d5819709SMark Johnston if (error == 0 && req->newptr != NULL) 934d5819709SMark Johnston error = vmmdev_create(buf, req->td->td_ucred); 935b9ef152bSMark Johnston free(buf, M_VMMDEV); 936b9ef152bSMark Johnston return (error); 937b9ef152bSMark Johnston } 938b9ef152bSMark Johnston SYSCTL_PROC(_hw_vmm, OID_AUTO, create, 939b9ef152bSMark Johnston CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 940b9ef152bSMark Johnston NULL, 0, sysctl_vmm_create, "A", 941b9ef152bSMark Johnston NULL); 942b9ef152bSMark Johnston 943b9ef152bSMark Johnston void 944b9ef152bSMark Johnston vmmdev_init(void) 945b9ef152bSMark Johnston { 946b9ef152bSMark Johnston pr_allow_flag = prison_add_allow(NULL, "vmm", NULL, 947b9ef152bSMark Johnston "Allow use of vmm in a jail."); 948b9ef152bSMark Johnston } 949b9ef152bSMark Johnston 950b9ef152bSMark Johnston int 951b9ef152bSMark Johnston vmmdev_cleanup(void) 952b9ef152bSMark Johnston { 953b9ef152bSMark Johnston int error; 954b9ef152bSMark Johnston 955b9ef152bSMark Johnston if (SLIST_EMPTY(&head)) 956b9ef152bSMark Johnston error = 0; 957b9ef152bSMark Johnston else 958b9ef152bSMark Johnston error = EBUSY; 959b9ef152bSMark Johnston 960b9ef152bSMark Johnston return (error); 961b9ef152bSMark Johnston } 962b9ef152bSMark Johnston 963b9ef152bSMark Johnston static int 964b9ef152bSMark Johnston devmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t len, 965b9ef152bSMark Johnston struct vm_object **objp, int nprot) 966b9ef152bSMark Johnston { 967b9ef152bSMark Johnston struct devmem_softc *dsc; 968b9ef152bSMark Johnston vm_ooffset_t first, last; 969b9ef152bSMark Johnston size_t seglen; 970b9ef152bSMark Johnston int error; 971b9ef152bSMark Johnston bool sysmem; 972b9ef152bSMark Johnston 973b9ef152bSMark Johnston dsc = cdev->si_drv1; 974b9ef152bSMark Johnston if (dsc == NULL) { 975b9ef152bSMark Johnston /* 'cdev' has been created but is not ready for use */ 976b9ef152bSMark Johnston return (ENXIO); 977b9ef152bSMark Johnston } 978b9ef152bSMark Johnston 979b9ef152bSMark Johnston first = *offset; 980b9ef152bSMark Johnston last = *offset + len; 981b9ef152bSMark Johnston if ((nprot & PROT_EXEC) || first < 0 || first >= last) 982b9ef152bSMark Johnston return (EINVAL); 983b9ef152bSMark Johnston 984b9ef152bSMark Johnston vm_slock_memsegs(dsc->sc->vm); 985b9ef152bSMark Johnston 986b9ef152bSMark Johnston error = vm_get_memseg(dsc->sc->vm, dsc->segid, &seglen, &sysmem, objp); 987b9ef152bSMark Johnston KASSERT(error == 0 && !sysmem && *objp != NULL, 988b9ef152bSMark Johnston ("%s: invalid devmem segment %d", __func__, dsc->segid)); 989b9ef152bSMark Johnston 990b9ef152bSMark Johnston if (seglen >= last) 991b9ef152bSMark Johnston vm_object_reference(*objp); 992b9ef152bSMark Johnston else 993b9ef152bSMark Johnston error = EINVAL; 994b9ef152bSMark Johnston 995b9ef152bSMark Johnston vm_unlock_memsegs(dsc->sc->vm); 996b9ef152bSMark Johnston return (error); 997b9ef152bSMark Johnston } 998b9ef152bSMark Johnston 999b9ef152bSMark Johnston static struct cdevsw devmemsw = { 1000b9ef152bSMark Johnston .d_name = "devmem", 1001b9ef152bSMark Johnston .d_version = D_VERSION, 1002b9ef152bSMark Johnston .d_mmap_single = devmem_mmap_single, 1003b9ef152bSMark Johnston }; 1004b9ef152bSMark Johnston 1005b9ef152bSMark Johnston static int 1006f4002135SMark Johnston devmem_create_cdev(struct vmmdev_softc *sc, int segid, char *devname) 1007b9ef152bSMark Johnston { 1008b9ef152bSMark Johnston struct devmem_softc *dsc; 1009b9ef152bSMark Johnston struct cdev *cdev; 1010f4002135SMark Johnston const char *vmname; 1011b9ef152bSMark Johnston int error; 1012b9ef152bSMark Johnston 1013f4002135SMark Johnston vmname = vm_name(sc->vm); 1014f4002135SMark Johnston 101572ea8f41SMark Johnston error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &devmemsw, sc->ucred, 1016b9ef152bSMark Johnston UID_ROOT, GID_WHEEL, 0600, "vmm.io/%s.%s", vmname, devname); 1017b9ef152bSMark Johnston if (error) 1018b9ef152bSMark Johnston return (error); 1019b9ef152bSMark Johnston 1020b9ef152bSMark Johnston dsc = malloc(sizeof(struct devmem_softc), M_VMMDEV, M_WAITOK | M_ZERO); 1021b9ef152bSMark Johnston 1022b9ef152bSMark Johnston mtx_lock(&vmmdev_mtx); 1023b9ef152bSMark Johnston if (sc->cdev == NULL) { 1024b9ef152bSMark Johnston /* virtual machine is being created or destroyed */ 1025b9ef152bSMark Johnston mtx_unlock(&vmmdev_mtx); 1026b9ef152bSMark Johnston free(dsc, M_VMMDEV); 1027b9ef152bSMark Johnston destroy_dev_sched_cb(cdev, NULL, 0); 1028b9ef152bSMark Johnston return (ENODEV); 1029b9ef152bSMark Johnston } 1030b9ef152bSMark Johnston 1031b9ef152bSMark Johnston dsc->segid = segid; 1032b9ef152bSMark Johnston dsc->name = devname; 1033b9ef152bSMark Johnston dsc->cdev = cdev; 1034b9ef152bSMark Johnston dsc->sc = sc; 1035b9ef152bSMark Johnston SLIST_INSERT_HEAD(&sc->devmem, dsc, link); 1036b9ef152bSMark Johnston mtx_unlock(&vmmdev_mtx); 1037b9ef152bSMark Johnston 1038b9ef152bSMark Johnston /* The 'cdev' is ready for use after 'si_drv1' is initialized */ 1039b9ef152bSMark Johnston cdev->si_drv1 = dsc; 1040b9ef152bSMark Johnston return (0); 1041b9ef152bSMark Johnston } 1042b9ef152bSMark Johnston 1043b9ef152bSMark Johnston static void 1044b9ef152bSMark Johnston devmem_destroy(void *arg) 1045b9ef152bSMark Johnston { 1046b9ef152bSMark Johnston struct devmem_softc *dsc = arg; 1047b9ef152bSMark Johnston 1048b9ef152bSMark Johnston KASSERT(dsc->cdev, ("%s: devmem cdev already destroyed", __func__)); 1049b9ef152bSMark Johnston dsc->cdev = NULL; 1050b9ef152bSMark Johnston dsc->sc = NULL; 1051b9ef152bSMark Johnston } 1052