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