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