vmm_dev.c revision 267427
1252190Srpaulo/*- 2252190Srpaulo * Copyright (c) 2011 NetApp, Inc. 3252190Srpaulo * All rights reserved. 4252190Srpaulo * 5252190Srpaulo * Redistribution and use in source and binary forms, with or without 6252190Srpaulo * modification, are permitted provided that the following conditions 7252190Srpaulo * are met: 8252190Srpaulo * 1. Redistributions of source code must retain the above copyright 9252190Srpaulo * notice, this list of conditions and the following disclaimer. 10252190Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11252190Srpaulo * notice, this list of conditions and the following disclaimer in the 12252190Srpaulo * documentation and/or other materials provided with the distribution. 13252190Srpaulo * 14252190Srpaulo * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15252190Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16252190Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17252190Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18252190Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19252190Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20252190Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21252190Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22252190Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23252190Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24252190Srpaulo * SUCH DAMAGE. 25252190Srpaulo * 26252190Srpaulo * $FreeBSD: stable/10/sys/amd64/vmm/vmm_dev.c 267427 2014-06-12 19:58:12Z jhb $ 27252190Srpaulo */ 28252190Srpaulo 29252190Srpaulo#include <sys/cdefs.h> 30252190Srpaulo__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/vmm_dev.c 267427 2014-06-12 19:58:12Z jhb $"); 31252190Srpaulo 32252190Srpaulo#include <sys/param.h> 33252190Srpaulo#include <sys/kernel.h> 34252190Srpaulo#include <sys/queue.h> 35252190Srpaulo#include <sys/lock.h> 36252190Srpaulo#include <sys/mutex.h> 37252190Srpaulo#include <sys/malloc.h> 38252190Srpaulo#include <sys/conf.h> 39252190Srpaulo#include <sys/sysctl.h> 40252190Srpaulo#include <sys/libkern.h> 41252190Srpaulo#include <sys/ioccom.h> 42252190Srpaulo#include <sys/mman.h> 43252190Srpaulo#include <sys/uio.h> 44252190Srpaulo 45252190Srpaulo#include <vm/vm.h> 46252190Srpaulo#include <vm/pmap.h> 47252190Srpaulo#include <vm/vm_map.h> 48252190Srpaulo 49252190Srpaulo#include <machine/vmparam.h> 50252190Srpaulo#include <machine/vmm.h> 51252190Srpaulo#include <machine/vmm_dev.h> 52252190Srpaulo 53252190Srpaulo#include "vmm_lapic.h" 54252190Srpaulo#include "vmm_stat.h" 55252190Srpaulo#include "vmm_mem.h" 56252190Srpaulo#include "io/ppt.h" 57252190Srpaulo#include "io/vioapic.h" 58252190Srpaulo#include "io/vhpet.h" 59252190Srpaulo 60252190Srpaulostruct vmmdev_softc { 61252190Srpaulo struct vm *vm; /* vm instance cookie */ 62252190Srpaulo struct cdev *cdev; 63252190Srpaulo SLIST_ENTRY(vmmdev_softc) link; 64252190Srpaulo int flags; 65252190Srpaulo}; 66252190Srpaulo#define VSC_LINKED 0x01 67252190Srpaulo 68252190Srpaulostatic SLIST_HEAD(, vmmdev_softc) head; 69252190Srpaulo 70252190Srpaulostatic struct mtx vmmdev_mtx; 71252190Srpaulo 72252190Srpaulostatic MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev"); 73252190Srpaulo 74252190SrpauloSYSCTL_DECL(_hw_vmm); 75252190Srpaulo 76252190Srpaulostatic struct vmmdev_softc * 77252190Srpaulovmmdev_lookup(const char *name) 78252190Srpaulo{ 79252190Srpaulo struct vmmdev_softc *sc; 80252190Srpaulo 81252190Srpaulo#ifdef notyet /* XXX kernel is not compiled with invariants */ 82252190Srpaulo mtx_assert(&vmmdev_mtx, MA_OWNED); 83252190Srpaulo#endif 84252190Srpaulo 85252190Srpaulo SLIST_FOREACH(sc, &head, link) { 86252190Srpaulo if (strcmp(name, vm_name(sc->vm)) == 0) 87252190Srpaulo break; 88252190Srpaulo } 89252190Srpaulo 90252190Srpaulo return (sc); 91252190Srpaulo} 92252190Srpaulo 93252190Srpaulostatic struct vmmdev_softc * 94252190Srpaulovmmdev_lookup2(struct cdev *cdev) 95252190Srpaulo{ 96252190Srpaulo 97252190Srpaulo return (cdev->si_drv1); 98252190Srpaulo} 99252190Srpaulo 100252190Srpaulostatic int 101252190Srpaulovmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) 102252190Srpaulo{ 103252190Srpaulo int error, off, c, prot; 104252190Srpaulo vm_paddr_t gpa; 105252190Srpaulo void *hpa, *cookie; 106252190Srpaulo struct vmmdev_softc *sc; 107252190Srpaulo 108252190Srpaulo static char zerobuf[PAGE_SIZE]; 109252190Srpaulo 110252190Srpaulo error = 0; 111252190Srpaulo sc = vmmdev_lookup2(cdev); 112252190Srpaulo if (sc == NULL) 113252190Srpaulo error = ENXIO; 114252190Srpaulo 115252190Srpaulo prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ); 116252190Srpaulo while (uio->uio_resid > 0 && error == 0) { 117252190Srpaulo gpa = uio->uio_offset; 118252190Srpaulo off = gpa & PAGE_MASK; 119252190Srpaulo c = min(uio->uio_resid, PAGE_SIZE - off); 120252190Srpaulo 121252190Srpaulo /* 122252190Srpaulo * The VM has a hole in its physical memory map. If we want to 123252190Srpaulo * use 'dd' to inspect memory beyond the hole we need to 124252190Srpaulo * provide bogus data for memory that lies in the hole. 125252190Srpaulo * 126252190Srpaulo * Since this device does not support lseek(2), dd(1) will 127252190Srpaulo * read(2) blocks of data to simulate the lseek(2). 128252190Srpaulo */ 129252190Srpaulo hpa = vm_gpa_hold(sc->vm, gpa, c, prot, &cookie); 130252190Srpaulo if (hpa == NULL) { 131252190Srpaulo if (uio->uio_rw == UIO_READ) 132252190Srpaulo error = uiomove(zerobuf, c, uio); 133252190Srpaulo else 134252190Srpaulo error = EFAULT; 135252190Srpaulo } else { 136252190Srpaulo error = uiomove(hpa, c, uio); 137252190Srpaulo vm_gpa_release(cookie); 138252190Srpaulo } 139252190Srpaulo } 140252190Srpaulo return (error); 141252190Srpaulo} 142252190Srpaulo 143252190Srpaulostatic int 144252190Srpaulovmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 145252190Srpaulo struct thread *td) 146252190Srpaulo{ 147252190Srpaulo int error, vcpu, state_changed; 148252190Srpaulo struct vmmdev_softc *sc; 149252190Srpaulo struct vm_memory_segment *seg; 150252190Srpaulo struct vm_register *vmreg; 151252190Srpaulo struct vm_seg_desc *vmsegdesc; 152252190Srpaulo struct vm_run *vmrun; 153252190Srpaulo struct vm_exception *vmexc; 154252190Srpaulo struct vm_lapic_irq *vmirq; 155252190Srpaulo struct vm_lapic_msi *vmmsi; 156252190Srpaulo struct vm_ioapic_irq *ioapic_irq; 157252190Srpaulo struct vm_capability *vmcap; 158252190Srpaulo struct vm_pptdev *pptdev; 159252190Srpaulo struct vm_pptdev_mmio *pptmmio; 160252190Srpaulo struct vm_pptdev_msi *pptmsi; 161252190Srpaulo struct vm_pptdev_msix *pptmsix; 162252190Srpaulo struct vm_nmi *vmnmi; 163252190Srpaulo struct vm_stats *vmstats; 164252190Srpaulo struct vm_stat_desc *statdesc; 165252190Srpaulo struct vm_x2apic *x2apic; 166252190Srpaulo struct vm_gpa_pte *gpapte; 167252190Srpaulo 168252190Srpaulo sc = vmmdev_lookup2(cdev); 169252190Srpaulo if (sc == NULL) 170252190Srpaulo return (ENXIO); 171252190Srpaulo 172252190Srpaulo vcpu = -1; 173252190Srpaulo state_changed = 0; 174252190Srpaulo 175252190Srpaulo /* 176252190Srpaulo * Some VMM ioctls can operate only on vcpus that are not running. 177252190Srpaulo */ 178252190Srpaulo switch (cmd) { 179252190Srpaulo case VM_RUN: 180252190Srpaulo case VM_GET_REGISTER: 181252190Srpaulo case VM_SET_REGISTER: 182252190Srpaulo case VM_GET_SEGMENT_DESCRIPTOR: 183252190Srpaulo case VM_SET_SEGMENT_DESCRIPTOR: 184252190Srpaulo case VM_INJECT_EXCEPTION: 185252190Srpaulo case VM_GET_CAPABILITY: 186252190Srpaulo case VM_SET_CAPABILITY: 187252190Srpaulo case VM_PPTDEV_MSI: 188252190Srpaulo case VM_PPTDEV_MSIX: 189252190Srpaulo case VM_SET_X2APIC_STATE: 190252190Srpaulo /* 191252190Srpaulo * XXX fragile, handle with care 192252190Srpaulo * Assumes that the first field of the ioctl data is the vcpu. 193252190Srpaulo */ 194252190Srpaulo vcpu = *(int *)data; 195252190Srpaulo if (vcpu < 0 || vcpu >= VM_MAXCPU) { 196252190Srpaulo error = EINVAL; 197252190Srpaulo goto done; 198252190Srpaulo } 199252190Srpaulo 200252190Srpaulo error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); 201252190Srpaulo if (error) 202252190Srpaulo goto done; 203252190Srpaulo 204252190Srpaulo state_changed = 1; 205252190Srpaulo break; 206252190Srpaulo 207252190Srpaulo case VM_MAP_PPTDEV_MMIO: 208252190Srpaulo case VM_BIND_PPTDEV: 209252190Srpaulo case VM_UNBIND_PPTDEV: 210252190Srpaulo case VM_MAP_MEMORY: 211252190Srpaulo /* 212252190Srpaulo * ioctls that operate on the entire virtual machine must 213252190Srpaulo * prevent all vcpus from running. 214252190Srpaulo */ 215252190Srpaulo error = 0; 216252190Srpaulo for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) { 217252190Srpaulo error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); 218252190Srpaulo if (error) 219252190Srpaulo break; 220252190Srpaulo } 221252190Srpaulo 222252190Srpaulo if (error) { 223252190Srpaulo while (--vcpu >= 0) 224252190Srpaulo vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); 225252190Srpaulo goto done; 226252190Srpaulo } 227252190Srpaulo 228252190Srpaulo state_changed = 2; 229252190Srpaulo break; 230252190Srpaulo 231252190Srpaulo default: 232252190Srpaulo break; 233252190Srpaulo } 234252190Srpaulo 235252190Srpaulo switch(cmd) { 236252190Srpaulo case VM_RUN: 237252190Srpaulo vmrun = (struct vm_run *)data; 238252190Srpaulo error = vm_run(sc->vm, vmrun); 239252190Srpaulo break; 240252190Srpaulo case VM_STAT_DESC: { 241252190Srpaulo statdesc = (struct vm_stat_desc *)data; 242252190Srpaulo error = vmm_stat_desc_copy(statdesc->index, 243252190Srpaulo statdesc->desc, sizeof(statdesc->desc)); 244252190Srpaulo break; 245252190Srpaulo } 246252190Srpaulo case VM_STATS: { 247252190Srpaulo CTASSERT(MAX_VM_STATS >= MAX_VMM_STAT_ELEMS); 248252190Srpaulo vmstats = (struct vm_stats *)data; 249252190Srpaulo getmicrotime(&vmstats->tv); 250252190Srpaulo error = vmm_stat_copy(sc->vm, vmstats->cpuid, 251252190Srpaulo &vmstats->num_entries, vmstats->statbuf); 252252190Srpaulo break; 253252190Srpaulo } 254252190Srpaulo case VM_PPTDEV_MSI: 255252190Srpaulo pptmsi = (struct vm_pptdev_msi *)data; 256252190Srpaulo error = ppt_setup_msi(sc->vm, pptmsi->vcpu, 257252190Srpaulo pptmsi->bus, pptmsi->slot, pptmsi->func, 258252190Srpaulo pptmsi->addr, pptmsi->msg, 259252190Srpaulo pptmsi->numvec); 260252190Srpaulo break; 261252190Srpaulo case VM_PPTDEV_MSIX: 262252190Srpaulo pptmsix = (struct vm_pptdev_msix *)data; 263252190Srpaulo error = ppt_setup_msix(sc->vm, pptmsix->vcpu, 264252190Srpaulo pptmsix->bus, pptmsix->slot, 265252190Srpaulo pptmsix->func, pptmsix->idx, 266252190Srpaulo pptmsix->addr, pptmsix->msg, 267252190Srpaulo pptmsix->vector_control); 268252190Srpaulo break; 269252190Srpaulo case VM_MAP_PPTDEV_MMIO: 270252190Srpaulo pptmmio = (struct vm_pptdev_mmio *)data; 271252190Srpaulo error = ppt_map_mmio(sc->vm, pptmmio->bus, pptmmio->slot, 272252190Srpaulo pptmmio->func, pptmmio->gpa, pptmmio->len, 273252190Srpaulo pptmmio->hpa); 274252190Srpaulo break; 275252190Srpaulo case VM_BIND_PPTDEV: 276252190Srpaulo pptdev = (struct vm_pptdev *)data; 277252190Srpaulo error = vm_assign_pptdev(sc->vm, pptdev->bus, pptdev->slot, 278252190Srpaulo pptdev->func); 279252190Srpaulo break; 280252190Srpaulo case VM_UNBIND_PPTDEV: 281252190Srpaulo pptdev = (struct vm_pptdev *)data; 282252190Srpaulo error = vm_unassign_pptdev(sc->vm, pptdev->bus, pptdev->slot, 283252190Srpaulo pptdev->func); 284252190Srpaulo break; 285252190Srpaulo case VM_INJECT_EXCEPTION: 286252190Srpaulo vmexc = (struct vm_exception *)data; 287252190Srpaulo error = vm_inject_exception(sc->vm, vmexc->cpuid, vmexc); 288252190Srpaulo break; 289252190Srpaulo case VM_INJECT_NMI: 290252190Srpaulo vmnmi = (struct vm_nmi *)data; 291252190Srpaulo error = vm_inject_nmi(sc->vm, vmnmi->cpuid); 292252190Srpaulo break; 293252190Srpaulo case VM_LAPIC_IRQ: 294252190Srpaulo vmirq = (struct vm_lapic_irq *)data; 295252190Srpaulo error = lapic_intr_edge(sc->vm, vmirq->cpuid, vmirq->vector); 296252190Srpaulo break; 297252190Srpaulo case VM_LAPIC_LOCAL_IRQ: 298252190Srpaulo vmirq = (struct vm_lapic_irq *)data; 299252190Srpaulo error = lapic_set_local_intr(sc->vm, vmirq->cpuid, 300252190Srpaulo vmirq->vector); 301252190Srpaulo break; 302252190Srpaulo case VM_LAPIC_MSI: 303252190Srpaulo vmmsi = (struct vm_lapic_msi *)data; 304252190Srpaulo error = lapic_intr_msi(sc->vm, vmmsi->addr, vmmsi->msg); 305252190Srpaulo break; 306252190Srpaulo case VM_IOAPIC_ASSERT_IRQ: 307252190Srpaulo ioapic_irq = (struct vm_ioapic_irq *)data; 308252190Srpaulo error = vioapic_assert_irq(sc->vm, ioapic_irq->irq); 309252190Srpaulo break; 310252190Srpaulo case VM_IOAPIC_DEASSERT_IRQ: 311252190Srpaulo ioapic_irq = (struct vm_ioapic_irq *)data; 312252190Srpaulo error = vioapic_deassert_irq(sc->vm, ioapic_irq->irq); 313252190Srpaulo break; 314252190Srpaulo case VM_IOAPIC_PULSE_IRQ: 315252190Srpaulo ioapic_irq = (struct vm_ioapic_irq *)data; 316252190Srpaulo error = vioapic_pulse_irq(sc->vm, ioapic_irq->irq); 317252190Srpaulo break; 318252190Srpaulo case VM_IOAPIC_PINCOUNT: 319252190Srpaulo *(int *)data = vioapic_pincount(sc->vm); 320252190Srpaulo break; 321252190Srpaulo case VM_MAP_MEMORY: 322252190Srpaulo seg = (struct vm_memory_segment *)data; 323252190Srpaulo error = vm_malloc(sc->vm, seg->gpa, seg->len); 324252190Srpaulo break; 325252190Srpaulo case VM_GET_MEMORY_SEG: 326252190Srpaulo seg = (struct vm_memory_segment *)data; 327252190Srpaulo seg->len = 0; 328252190Srpaulo (void)vm_gpabase2memseg(sc->vm, seg->gpa, seg); 329252190Srpaulo error = 0; 330252190Srpaulo break; 331252190Srpaulo case VM_GET_REGISTER: 332252190Srpaulo vmreg = (struct vm_register *)data; 333252190Srpaulo error = vm_get_register(sc->vm, vmreg->cpuid, vmreg->regnum, 334252190Srpaulo &vmreg->regval); 335252190Srpaulo break; 336252190Srpaulo case VM_SET_REGISTER: 337252190Srpaulo vmreg = (struct vm_register *)data; 338252190Srpaulo error = vm_set_register(sc->vm, vmreg->cpuid, vmreg->regnum, 339252190Srpaulo vmreg->regval); 340252190Srpaulo break; 341252190Srpaulo case VM_SET_SEGMENT_DESCRIPTOR: 342252190Srpaulo vmsegdesc = (struct vm_seg_desc *)data; 343252190Srpaulo error = vm_set_seg_desc(sc->vm, vmsegdesc->cpuid, 344252190Srpaulo vmsegdesc->regnum, 345252190Srpaulo &vmsegdesc->desc); 346252190Srpaulo break; 347252190Srpaulo case VM_GET_SEGMENT_DESCRIPTOR: 348252190Srpaulo vmsegdesc = (struct vm_seg_desc *)data; 349252190Srpaulo error = vm_get_seg_desc(sc->vm, vmsegdesc->cpuid, 350252190Srpaulo vmsegdesc->regnum, 351252190Srpaulo &vmsegdesc->desc); 352252190Srpaulo break; 353252190Srpaulo case VM_GET_CAPABILITY: 354252190Srpaulo vmcap = (struct vm_capability *)data; 355252190Srpaulo error = vm_get_capability(sc->vm, vmcap->cpuid, 356252190Srpaulo vmcap->captype, 357252190Srpaulo &vmcap->capval); 358252190Srpaulo break; 359252190Srpaulo case VM_SET_CAPABILITY: 360252190Srpaulo vmcap = (struct vm_capability *)data; 361252190Srpaulo error = vm_set_capability(sc->vm, vmcap->cpuid, 362252190Srpaulo vmcap->captype, 363252190Srpaulo vmcap->capval); 364252190Srpaulo break; 365252190Srpaulo case VM_SET_X2APIC_STATE: 366252190Srpaulo x2apic = (struct vm_x2apic *)data; 367252190Srpaulo error = vm_set_x2apic_state(sc->vm, 368252190Srpaulo x2apic->cpuid, x2apic->state); 369252190Srpaulo break; 370252190Srpaulo case VM_GET_X2APIC_STATE: 371252190Srpaulo x2apic = (struct vm_x2apic *)data; 372252190Srpaulo error = vm_get_x2apic_state(sc->vm, 373252190Srpaulo x2apic->cpuid, &x2apic->state); 374252190Srpaulo break; 375252190Srpaulo case VM_GET_GPA_PMAP: 376252190Srpaulo gpapte = (struct vm_gpa_pte *)data; 377252190Srpaulo pmap_get_mapping(vmspace_pmap(vm_get_vmspace(sc->vm)), 378252190Srpaulo gpapte->gpa, gpapte->pte, &gpapte->ptenum); 379252190Srpaulo error = 0; 380252190Srpaulo break; 381252190Srpaulo case VM_GET_HPET_CAPABILITIES: 382252190Srpaulo error = vhpet_getcap((struct vm_hpet_cap *)data); 383252190Srpaulo break; 384252190Srpaulo default: 385252190Srpaulo error = ENOTTY; 386252190Srpaulo break; 387252190Srpaulo } 388252190Srpaulo 389252190Srpaulo if (state_changed == 1) { 390252190Srpaulo vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); 391252190Srpaulo } else if (state_changed == 2) { 392252190Srpaulo for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) 393252190Srpaulo vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); 394252190Srpaulo } 395252190Srpaulo 396252190Srpaulodone: 397252190Srpaulo /* Make sure that no handler returns a bogus value like ERESTART */ 398252190Srpaulo KASSERT(error >= 0, ("vmmdev_ioctl: invalid error return %d", error)); 399252190Srpaulo return (error); 400252190Srpaulo} 401252190Srpaulo 402252190Srpaulostatic int 403252190Srpaulovmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, 404252190Srpaulo vm_size_t size, struct vm_object **object, int nprot) 405252190Srpaulo{ 406252190Srpaulo int error; 407252190Srpaulo struct vmmdev_softc *sc; 408252190Srpaulo 409252190Srpaulo sc = vmmdev_lookup2(cdev); 410252190Srpaulo if (sc != NULL && (nprot & PROT_EXEC) == 0) 411252190Srpaulo error = vm_get_memobj(sc->vm, *offset, size, offset, object); 412252190Srpaulo else 413252190Srpaulo error = EINVAL; 414252190Srpaulo 415252190Srpaulo return (error); 416252190Srpaulo} 417252190Srpaulo 418252190Srpaulostatic void 419252190Srpaulovmmdev_destroy(void *arg) 420252190Srpaulo{ 421252190Srpaulo 422252190Srpaulo struct vmmdev_softc *sc = arg; 423252190Srpaulo 424252190Srpaulo if (sc->cdev != NULL) 425252190Srpaulo destroy_dev(sc->cdev); 426252190Srpaulo 427252190Srpaulo if (sc->vm != NULL) 428252190Srpaulo vm_destroy(sc->vm); 429252190Srpaulo 430252190Srpaulo if ((sc->flags & VSC_LINKED) != 0) { 431252190Srpaulo mtx_lock(&vmmdev_mtx); 432252190Srpaulo SLIST_REMOVE(&head, sc, vmmdev_softc, link); 433252190Srpaulo mtx_unlock(&vmmdev_mtx); 434252190Srpaulo } 435252190Srpaulo 436252190Srpaulo free(sc, M_VMMDEV); 437252190Srpaulo} 438252190Srpaulo 439252190Srpaulostatic int 440252190Srpaulosysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) 441252190Srpaulo{ 442252190Srpaulo int error; 443252190Srpaulo char buf[VM_MAX_NAMELEN]; 444252190Srpaulo struct vmmdev_softc *sc; 445252190Srpaulo struct cdev *cdev; 446252190Srpaulo 447252190Srpaulo strlcpy(buf, "beavis", sizeof(buf)); 448252190Srpaulo error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 449252190Srpaulo if (error != 0 || req->newptr == NULL) 450252190Srpaulo return (error); 451252190Srpaulo 452252190Srpaulo mtx_lock(&vmmdev_mtx); 453252190Srpaulo sc = vmmdev_lookup(buf); 454252190Srpaulo if (sc == NULL || sc->cdev == NULL) { 455252190Srpaulo mtx_unlock(&vmmdev_mtx); 456252190Srpaulo return (EINVAL); 457252190Srpaulo } 458252190Srpaulo 459252190Srpaulo /* 460252190Srpaulo * The 'cdev' will be destroyed asynchronously when 'si_threadcount' 461252190Srpaulo * goes down to 0 so we should not do it again in the callback. 462252190Srpaulo */ 463252190Srpaulo cdev = sc->cdev; 464252190Srpaulo sc->cdev = NULL; 465252190Srpaulo mtx_unlock(&vmmdev_mtx); 466252190Srpaulo 467252190Srpaulo /* 468252190Srpaulo * Schedule the 'cdev' to be destroyed: 469252190Srpaulo * 470252190Srpaulo * - any new operations on this 'cdev' will return an error (ENXIO). 471252190Srpaulo * 472252190Srpaulo * - when the 'si_threadcount' dwindles down to zero the 'cdev' will 473252190Srpaulo * be destroyed and the callback will be invoked in a taskqueue 474252190Srpaulo * context. 475252190Srpaulo */ 476252190Srpaulo destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); 477252190Srpaulo 478252190Srpaulo return (0); 479252190Srpaulo} 480252190SrpauloSYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, CTLTYPE_STRING | CTLFLAG_RW, 481252190Srpaulo NULL, 0, sysctl_vmm_destroy, "A", NULL); 482252190Srpaulo 483252190Srpaulostatic struct cdevsw vmmdevsw = { 484252190Srpaulo .d_name = "vmmdev", 485252190Srpaulo .d_version = D_VERSION, 486252190Srpaulo .d_ioctl = vmmdev_ioctl, 487252190Srpaulo .d_mmap_single = vmmdev_mmap_single, 488252190Srpaulo .d_read = vmmdev_rw, 489252190Srpaulo .d_write = vmmdev_rw, 490252190Srpaulo}; 491252190Srpaulo 492252190Srpaulostatic int 493252190Srpaulosysctl_vmm_create(SYSCTL_HANDLER_ARGS) 494252190Srpaulo{ 495252190Srpaulo int error; 496252190Srpaulo struct vm *vm; 497252190Srpaulo struct cdev *cdev; 498252190Srpaulo struct vmmdev_softc *sc, *sc2; 499252190Srpaulo char buf[VM_MAX_NAMELEN]; 500252190Srpaulo 501252190Srpaulo strlcpy(buf, "beavis", sizeof(buf)); 502252190Srpaulo error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 503252190Srpaulo if (error != 0 || req->newptr == NULL) 504252190Srpaulo return (error); 505252190Srpaulo 506252190Srpaulo mtx_lock(&vmmdev_mtx); 507252190Srpaulo sc = vmmdev_lookup(buf); 508252190Srpaulo mtx_unlock(&vmmdev_mtx); 509252190Srpaulo if (sc != NULL) 510252190Srpaulo return (EEXIST); 511252190Srpaulo 512252190Srpaulo error = vm_create(buf, &vm); 513252190Srpaulo if (error != 0) 514252190Srpaulo return (error); 515252190Srpaulo 516252190Srpaulo sc = malloc(sizeof(struct vmmdev_softc), M_VMMDEV, M_WAITOK | M_ZERO); 517252190Srpaulo sc->vm = vm; 518252190Srpaulo 519252190Srpaulo /* 520252190Srpaulo * Lookup the name again just in case somebody sneaked in when we 521252190Srpaulo * dropped the lock. 522252190Srpaulo */ 523252190Srpaulo mtx_lock(&vmmdev_mtx); 524252190Srpaulo sc2 = vmmdev_lookup(buf); 525252190Srpaulo if (sc2 == NULL) { 526252190Srpaulo SLIST_INSERT_HEAD(&head, sc, link); 527252190Srpaulo sc->flags |= VSC_LINKED; 528252190Srpaulo } 529252190Srpaulo mtx_unlock(&vmmdev_mtx); 530252190Srpaulo 531252190Srpaulo if (sc2 != NULL) { 532252190Srpaulo vmmdev_destroy(sc); 533252190Srpaulo return (EEXIST); 534252190Srpaulo } 535252190Srpaulo 536252190Srpaulo error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, NULL, 537252190Srpaulo UID_ROOT, GID_WHEEL, 0600, "vmm/%s", buf); 538252190Srpaulo if (error != 0) { 539252190Srpaulo vmmdev_destroy(sc); 540252190Srpaulo return (error); 541252190Srpaulo } 542252190Srpaulo 543252190Srpaulo mtx_lock(&vmmdev_mtx); 544252190Srpaulo sc->cdev = cdev; 545252190Srpaulo sc->cdev->si_drv1 = sc; 546252190Srpaulo mtx_unlock(&vmmdev_mtx); 547252190Srpaulo 548252190Srpaulo return (0); 549252190Srpaulo} 550252190SrpauloSYSCTL_PROC(_hw_vmm, OID_AUTO, create, CTLTYPE_STRING | CTLFLAG_RW, 551252190Srpaulo NULL, 0, sysctl_vmm_create, "A", NULL); 552252190Srpaulo 553252190Srpaulovoid 554252190Srpaulovmmdev_init(void) 555252190Srpaulo{ 556252190Srpaulo mtx_init(&vmmdev_mtx, "vmm device mutex", NULL, MTX_DEF); 557252190Srpaulo} 558252190Srpaulo 559252190Srpauloint 560252190Srpaulovmmdev_cleanup(void) 561252190Srpaulo{ 562252190Srpaulo int error; 563252190Srpaulo 564252190Srpaulo if (SLIST_EMPTY(&head)) 565252190Srpaulo error = 0; 566252190Srpaulo else 567252190Srpaulo error = EBUSY; 568252190Srpaulo 569252190Srpaulo return (error); 570252190Srpaulo} 571252190Srpaulo