vmm_dev.c revision 267393
1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD: stable/10/sys/amd64/vmm/vmm_dev.c 267393 2014-06-12 13:13:15Z jhb $ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/vmm_dev.c 267393 2014-06-12 13:13:15Z jhb $"); 31221828Sgrehan 32221828Sgrehan#include <sys/param.h> 33221828Sgrehan#include <sys/kernel.h> 34221828Sgrehan#include <sys/queue.h> 35221828Sgrehan#include <sys/lock.h> 36221828Sgrehan#include <sys/mutex.h> 37221828Sgrehan#include <sys/malloc.h> 38221828Sgrehan#include <sys/conf.h> 39221828Sgrehan#include <sys/sysctl.h> 40221828Sgrehan#include <sys/libkern.h> 41221828Sgrehan#include <sys/ioccom.h> 42221828Sgrehan#include <sys/mman.h> 43221828Sgrehan#include <sys/uio.h> 44221828Sgrehan 45221828Sgrehan#include <vm/vm.h> 46221828Sgrehan#include <vm/pmap.h> 47256072Sneel#include <vm/vm_map.h> 48221828Sgrehan 49221828Sgrehan#include <machine/vmparam.h> 50261088Sjhb#include <machine/vmm.h> 51261088Sjhb#include <machine/vmm_dev.h> 52221828Sgrehan 53221828Sgrehan#include "vmm_lapic.h" 54221828Sgrehan#include "vmm_stat.h" 55239700Sgrehan#include "vmm_mem.h" 56221828Sgrehan#include "io/ppt.h" 57261088Sjhb#include "io/vioapic.h" 58261088Sjhb#include "io/vhpet.h" 59221828Sgrehan 60221828Sgrehanstruct vmmdev_softc { 61221828Sgrehan struct vm *vm; /* vm instance cookie */ 62221828Sgrehan struct cdev *cdev; 63221828Sgrehan SLIST_ENTRY(vmmdev_softc) link; 64256651Sneel int flags; 65221828Sgrehan}; 66256651Sneel#define VSC_LINKED 0x01 67256651Sneel 68221828Sgrehanstatic SLIST_HEAD(, vmmdev_softc) head; 69221828Sgrehan 70221828Sgrehanstatic struct mtx vmmdev_mtx; 71221828Sgrehan 72221828Sgrehanstatic MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev"); 73221828Sgrehan 74221828SgrehanSYSCTL_DECL(_hw_vmm); 75221828Sgrehan 76221828Sgrehanstatic struct vmmdev_softc * 77221828Sgrehanvmmdev_lookup(const char *name) 78221828Sgrehan{ 79221828Sgrehan struct vmmdev_softc *sc; 80221828Sgrehan 81221828Sgrehan#ifdef notyet /* XXX kernel is not compiled with invariants */ 82221828Sgrehan mtx_assert(&vmmdev_mtx, MA_OWNED); 83221828Sgrehan#endif 84221828Sgrehan 85221828Sgrehan SLIST_FOREACH(sc, &head, link) { 86221828Sgrehan if (strcmp(name, vm_name(sc->vm)) == 0) 87221828Sgrehan break; 88221828Sgrehan } 89221828Sgrehan 90221828Sgrehan return (sc); 91221828Sgrehan} 92221828Sgrehan 93221828Sgrehanstatic struct vmmdev_softc * 94221828Sgrehanvmmdev_lookup2(struct cdev *cdev) 95221828Sgrehan{ 96221828Sgrehan 97241454Sneel return (cdev->si_drv1); 98221828Sgrehan} 99221828Sgrehan 100221828Sgrehanstatic int 101221828Sgrehanvmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) 102221828Sgrehan{ 103256072Sneel int error, off, c, prot; 104256072Sneel vm_paddr_t gpa; 105256072Sneel void *hpa, *cookie; 106221828Sgrehan struct vmmdev_softc *sc; 107221828Sgrehan 108221828Sgrehan static char zerobuf[PAGE_SIZE]; 109221828Sgrehan 110221828Sgrehan error = 0; 111221828Sgrehan sc = vmmdev_lookup2(cdev); 112241454Sneel if (sc == NULL) 113241454Sneel error = ENXIO; 114221828Sgrehan 115256072Sneel prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ); 116221828Sgrehan while (uio->uio_resid > 0 && error == 0) { 117221828Sgrehan gpa = uio->uio_offset; 118221828Sgrehan off = gpa & PAGE_MASK; 119221828Sgrehan c = min(uio->uio_resid, PAGE_SIZE - off); 120221828Sgrehan 121221828Sgrehan /* 122221828Sgrehan * The VM has a hole in its physical memory map. If we want to 123221828Sgrehan * use 'dd' to inspect memory beyond the hole we need to 124221828Sgrehan * provide bogus data for memory that lies in the hole. 125221828Sgrehan * 126221828Sgrehan * Since this device does not support lseek(2), dd(1) will 127221828Sgrehan * read(2) blocks of data to simulate the lseek(2). 128221828Sgrehan */ 129256072Sneel hpa = vm_gpa_hold(sc->vm, gpa, c, prot, &cookie); 130256072Sneel if (hpa == NULL) { 131221828Sgrehan if (uio->uio_rw == UIO_READ) 132221828Sgrehan error = uiomove(zerobuf, c, uio); 133221828Sgrehan else 134221828Sgrehan error = EFAULT; 135256072Sneel } else { 136256072Sneel error = uiomove(hpa, c, uio); 137256072Sneel vm_gpa_release(cookie); 138256072Sneel } 139221828Sgrehan } 140221828Sgrehan return (error); 141221828Sgrehan} 142221828Sgrehan 143221828Sgrehanstatic int 144221828Sgrehanvmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 145221828Sgrehan struct thread *td) 146221828Sgrehan{ 147241489Sneel int error, vcpu, state_changed; 148221828Sgrehan struct vmmdev_softc *sc; 149221828Sgrehan struct vm_memory_segment *seg; 150221828Sgrehan struct vm_register *vmreg; 151261088Sjhb struct vm_seg_desc *vmsegdesc; 152221828Sgrehan struct vm_run *vmrun; 153221828Sgrehan struct vm_event *vmevent; 154221828Sgrehan struct vm_lapic_irq *vmirq; 155262350Sjhb struct vm_lapic_msi *vmmsi; 156261088Sjhb struct vm_ioapic_irq *ioapic_irq; 157221828Sgrehan struct vm_capability *vmcap; 158221828Sgrehan struct vm_pptdev *pptdev; 159221828Sgrehan struct vm_pptdev_mmio *pptmmio; 160221828Sgrehan struct vm_pptdev_msi *pptmsi; 161234761Sgrehan struct vm_pptdev_msix *pptmsix; 162221828Sgrehan struct vm_nmi *vmnmi; 163221828Sgrehan struct vm_stats *vmstats; 164221828Sgrehan struct vm_stat_desc *statdesc; 165240922Sneel struct vm_x2apic *x2apic; 166256072Sneel struct vm_gpa_pte *gpapte; 167221828Sgrehan 168221828Sgrehan sc = vmmdev_lookup2(cdev); 169241489Sneel if (sc == NULL) 170221828Sgrehan return (ENXIO); 171221828Sgrehan 172241489Sneel vcpu = -1; 173241489Sneel state_changed = 0; 174241489Sneel 175221828Sgrehan /* 176221828Sgrehan * Some VMM ioctls can operate only on vcpus that are not running. 177221828Sgrehan */ 178221828Sgrehan switch (cmd) { 179221828Sgrehan case VM_RUN: 180221828Sgrehan case VM_GET_REGISTER: 181221828Sgrehan case VM_SET_REGISTER: 182221828Sgrehan case VM_GET_SEGMENT_DESCRIPTOR: 183221828Sgrehan case VM_SET_SEGMENT_DESCRIPTOR: 184221828Sgrehan case VM_INJECT_EVENT: 185221828Sgrehan case VM_GET_CAPABILITY: 186221828Sgrehan case VM_SET_CAPABILITY: 187221828Sgrehan case VM_PPTDEV_MSI: 188241489Sneel case VM_PPTDEV_MSIX: 189240922Sneel case VM_SET_X2APIC_STATE: 190221828Sgrehan /* 191221828Sgrehan * XXX fragile, handle with care 192221828Sgrehan * Assumes that the first field of the ioctl data is the vcpu. 193221828Sgrehan */ 194221828Sgrehan vcpu = *(int *)data; 195221828Sgrehan if (vcpu < 0 || vcpu >= VM_MAXCPU) { 196221828Sgrehan error = EINVAL; 197221828Sgrehan goto done; 198221828Sgrehan } 199221828Sgrehan 200266393Sjhb error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); 201241489Sneel if (error) 202221828Sgrehan goto done; 203241489Sneel 204241489Sneel state_changed = 1; 205241489Sneel break; 206241489Sneel 207241489Sneel case VM_MAP_PPTDEV_MMIO: 208241489Sneel case VM_BIND_PPTDEV: 209241489Sneel case VM_UNBIND_PPTDEV: 210241489Sneel case VM_MAP_MEMORY: 211241489Sneel /* 212241489Sneel * ioctls that operate on the entire virtual machine must 213241489Sneel * prevent all vcpus from running. 214241489Sneel */ 215241489Sneel error = 0; 216241489Sneel for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) { 217266393Sjhb error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); 218241489Sneel if (error) 219241489Sneel break; 220221828Sgrehan } 221241489Sneel 222241489Sneel if (error) { 223241489Sneel while (--vcpu >= 0) 224266393Sjhb vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); 225241489Sneel goto done; 226241489Sneel } 227241489Sneel 228241489Sneel state_changed = 2; 229221828Sgrehan break; 230241489Sneel 231221828Sgrehan default: 232221828Sgrehan break; 233221828Sgrehan } 234221828Sgrehan 235221828Sgrehan switch(cmd) { 236221828Sgrehan case VM_RUN: 237221828Sgrehan vmrun = (struct vm_run *)data; 238221828Sgrehan error = vm_run(sc->vm, vmrun); 239221828Sgrehan break; 240221828Sgrehan case VM_STAT_DESC: { 241221828Sgrehan statdesc = (struct vm_stat_desc *)data; 242250427Sneel error = vmm_stat_desc_copy(statdesc->index, 243250427Sneel statdesc->desc, sizeof(statdesc->desc)); 244221828Sgrehan break; 245221828Sgrehan } 246221828Sgrehan case VM_STATS: { 247250427Sneel CTASSERT(MAX_VM_STATS >= MAX_VMM_STAT_ELEMS); 248221828Sgrehan vmstats = (struct vm_stats *)data; 249221828Sgrehan getmicrotime(&vmstats->tv); 250221828Sgrehan error = vmm_stat_copy(sc->vm, vmstats->cpuid, 251221828Sgrehan &vmstats->num_entries, vmstats->statbuf); 252221828Sgrehan break; 253221828Sgrehan } 254221828Sgrehan case VM_PPTDEV_MSI: 255221828Sgrehan pptmsi = (struct vm_pptdev_msi *)data; 256221828Sgrehan error = ppt_setup_msi(sc->vm, pptmsi->vcpu, 257221828Sgrehan pptmsi->bus, pptmsi->slot, pptmsi->func, 258262350Sjhb pptmsi->addr, pptmsi->msg, 259221828Sgrehan pptmsi->numvec); 260221828Sgrehan break; 261234761Sgrehan case VM_PPTDEV_MSIX: 262234761Sgrehan pptmsix = (struct vm_pptdev_msix *)data; 263234761Sgrehan error = ppt_setup_msix(sc->vm, pptmsix->vcpu, 264234761Sgrehan pptmsix->bus, pptmsix->slot, 265234761Sgrehan pptmsix->func, pptmsix->idx, 266262350Sjhb pptmsix->addr, pptmsix->msg, 267262350Sjhb pptmsix->vector_control); 268234761Sgrehan break; 269221828Sgrehan case VM_MAP_PPTDEV_MMIO: 270221828Sgrehan pptmmio = (struct vm_pptdev_mmio *)data; 271221828Sgrehan error = ppt_map_mmio(sc->vm, pptmmio->bus, pptmmio->slot, 272221828Sgrehan pptmmio->func, pptmmio->gpa, pptmmio->len, 273221828Sgrehan pptmmio->hpa); 274221828Sgrehan break; 275221828Sgrehan case VM_BIND_PPTDEV: 276221828Sgrehan pptdev = (struct vm_pptdev *)data; 277256072Sneel error = vm_assign_pptdev(sc->vm, pptdev->bus, pptdev->slot, 278256072Sneel pptdev->func); 279221828Sgrehan break; 280221828Sgrehan case VM_UNBIND_PPTDEV: 281221828Sgrehan pptdev = (struct vm_pptdev *)data; 282256072Sneel error = vm_unassign_pptdev(sc->vm, pptdev->bus, pptdev->slot, 283256072Sneel pptdev->func); 284221828Sgrehan break; 285221828Sgrehan case VM_INJECT_EVENT: 286221828Sgrehan vmevent = (struct vm_event *)data; 287221828Sgrehan error = vm_inject_event(sc->vm, vmevent->cpuid, vmevent->type, 288221828Sgrehan vmevent->vector, 289221828Sgrehan vmevent->error_code, 290221828Sgrehan vmevent->error_code_valid); 291221828Sgrehan break; 292221828Sgrehan case VM_INJECT_NMI: 293221828Sgrehan vmnmi = (struct vm_nmi *)data; 294221828Sgrehan error = vm_inject_nmi(sc->vm, vmnmi->cpuid); 295221828Sgrehan break; 296221828Sgrehan case VM_LAPIC_IRQ: 297221828Sgrehan vmirq = (struct vm_lapic_irq *)data; 298261088Sjhb error = lapic_intr_edge(sc->vm, vmirq->cpuid, vmirq->vector); 299221828Sgrehan break; 300262350Sjhb case VM_LAPIC_LOCAL_IRQ: 301262350Sjhb vmirq = (struct vm_lapic_irq *)data; 302262350Sjhb error = lapic_set_local_intr(sc->vm, vmirq->cpuid, 303262350Sjhb vmirq->vector); 304262350Sjhb break; 305262350Sjhb case VM_LAPIC_MSI: 306262350Sjhb vmmsi = (struct vm_lapic_msi *)data; 307262350Sjhb error = lapic_intr_msi(sc->vm, vmmsi->addr, vmmsi->msg); 308262350Sjhb break; 309261088Sjhb case VM_IOAPIC_ASSERT_IRQ: 310261088Sjhb ioapic_irq = (struct vm_ioapic_irq *)data; 311261088Sjhb error = vioapic_assert_irq(sc->vm, ioapic_irq->irq); 312261088Sjhb break; 313261088Sjhb case VM_IOAPIC_DEASSERT_IRQ: 314261088Sjhb ioapic_irq = (struct vm_ioapic_irq *)data; 315261088Sjhb error = vioapic_deassert_irq(sc->vm, ioapic_irq->irq); 316261088Sjhb break; 317261088Sjhb case VM_IOAPIC_PULSE_IRQ: 318261088Sjhb ioapic_irq = (struct vm_ioapic_irq *)data; 319261088Sjhb error = vioapic_pulse_irq(sc->vm, ioapic_irq->irq); 320261088Sjhb break; 321267393Sjhb case VM_IOAPIC_PINCOUNT: 322267393Sjhb *(int *)data = vioapic_pincount(sc->vm); 323267393Sjhb break; 324221828Sgrehan case VM_MAP_MEMORY: 325221828Sgrehan seg = (struct vm_memory_segment *)data; 326241041Sneel error = vm_malloc(sc->vm, seg->gpa, seg->len); 327221828Sgrehan break; 328221828Sgrehan case VM_GET_MEMORY_SEG: 329221828Sgrehan seg = (struct vm_memory_segment *)data; 330241178Sneel seg->len = 0; 331221828Sgrehan (void)vm_gpabase2memseg(sc->vm, seg->gpa, seg); 332221828Sgrehan error = 0; 333221828Sgrehan break; 334221828Sgrehan case VM_GET_REGISTER: 335221828Sgrehan vmreg = (struct vm_register *)data; 336221828Sgrehan error = vm_get_register(sc->vm, vmreg->cpuid, vmreg->regnum, 337221828Sgrehan &vmreg->regval); 338221828Sgrehan break; 339221828Sgrehan case VM_SET_REGISTER: 340221828Sgrehan vmreg = (struct vm_register *)data; 341221828Sgrehan error = vm_set_register(sc->vm, vmreg->cpuid, vmreg->regnum, 342221828Sgrehan vmreg->regval); 343221828Sgrehan break; 344221828Sgrehan case VM_SET_SEGMENT_DESCRIPTOR: 345221828Sgrehan vmsegdesc = (struct vm_seg_desc *)data; 346221828Sgrehan error = vm_set_seg_desc(sc->vm, vmsegdesc->cpuid, 347221828Sgrehan vmsegdesc->regnum, 348221828Sgrehan &vmsegdesc->desc); 349221828Sgrehan break; 350221828Sgrehan case VM_GET_SEGMENT_DESCRIPTOR: 351221828Sgrehan vmsegdesc = (struct vm_seg_desc *)data; 352221828Sgrehan error = vm_get_seg_desc(sc->vm, vmsegdesc->cpuid, 353221828Sgrehan vmsegdesc->regnum, 354221828Sgrehan &vmsegdesc->desc); 355221828Sgrehan break; 356221828Sgrehan case VM_GET_CAPABILITY: 357221828Sgrehan vmcap = (struct vm_capability *)data; 358221828Sgrehan error = vm_get_capability(sc->vm, vmcap->cpuid, 359221828Sgrehan vmcap->captype, 360221828Sgrehan &vmcap->capval); 361221828Sgrehan break; 362221828Sgrehan case VM_SET_CAPABILITY: 363221828Sgrehan vmcap = (struct vm_capability *)data; 364221828Sgrehan error = vm_set_capability(sc->vm, vmcap->cpuid, 365221828Sgrehan vmcap->captype, 366221828Sgrehan vmcap->capval); 367221828Sgrehan break; 368240922Sneel case VM_SET_X2APIC_STATE: 369240922Sneel x2apic = (struct vm_x2apic *)data; 370240922Sneel error = vm_set_x2apic_state(sc->vm, 371240922Sneel x2apic->cpuid, x2apic->state); 372240922Sneel break; 373240922Sneel case VM_GET_X2APIC_STATE: 374240922Sneel x2apic = (struct vm_x2apic *)data; 375240922Sneel error = vm_get_x2apic_state(sc->vm, 376240922Sneel x2apic->cpuid, &x2apic->state); 377240922Sneel break; 378256072Sneel case VM_GET_GPA_PMAP: 379256072Sneel gpapte = (struct vm_gpa_pte *)data; 380256072Sneel pmap_get_mapping(vmspace_pmap(vm_get_vmspace(sc->vm)), 381256072Sneel gpapte->gpa, gpapte->pte, &gpapte->ptenum); 382256072Sneel error = 0; 383256072Sneel break; 384261088Sjhb case VM_GET_HPET_CAPABILITIES: 385261088Sjhb error = vhpet_getcap((struct vm_hpet_cap *)data); 386261088Sjhb break; 387221828Sgrehan default: 388221828Sgrehan error = ENOTTY; 389221828Sgrehan break; 390221828Sgrehan } 391241489Sneel 392241489Sneel if (state_changed == 1) { 393266393Sjhb vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); 394241489Sneel } else if (state_changed == 2) { 395241489Sneel for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) 396266393Sjhb vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); 397241489Sneel } 398241489Sneel 399221828Sgrehandone: 400256072Sneel /* Make sure that no handler returns a bogus value like ERESTART */ 401256072Sneel KASSERT(error >= 0, ("vmmdev_ioctl: invalid error return %d", error)); 402221828Sgrehan return (error); 403221828Sgrehan} 404221828Sgrehan 405221828Sgrehanstatic int 406256072Sneelvmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, 407256072Sneel vm_size_t size, struct vm_object **object, int nprot) 408221828Sgrehan{ 409221828Sgrehan int error; 410221828Sgrehan struct vmmdev_softc *sc; 411221828Sgrehan 412221828Sgrehan sc = vmmdev_lookup2(cdev); 413256072Sneel if (sc != NULL && (nprot & PROT_EXEC) == 0) 414256072Sneel error = vm_get_memobj(sc->vm, *offset, size, offset, object); 415256072Sneel else 416256072Sneel error = EINVAL; 417221828Sgrehan 418221828Sgrehan return (error); 419221828Sgrehan} 420221828Sgrehan 421221828Sgrehanstatic void 422256651Sneelvmmdev_destroy(void *arg) 423221828Sgrehan{ 424221828Sgrehan 425256651Sneel struct vmmdev_softc *sc = arg; 426256651Sneel 427256651Sneel if (sc->cdev != NULL) 428241454Sneel destroy_dev(sc->cdev); 429241454Sneel 430256651Sneel if (sc->vm != NULL) 431241454Sneel vm_destroy(sc->vm); 432241454Sneel 433256651Sneel if ((sc->flags & VSC_LINKED) != 0) { 434241454Sneel mtx_lock(&vmmdev_mtx); 435241454Sneel SLIST_REMOVE(&head, sc, vmmdev_softc, link); 436241454Sneel mtx_unlock(&vmmdev_mtx); 437241454Sneel } 438241454Sneel 439221828Sgrehan free(sc, M_VMMDEV); 440221828Sgrehan} 441221828Sgrehan 442221828Sgrehanstatic int 443221828Sgrehansysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) 444221828Sgrehan{ 445221828Sgrehan int error; 446221828Sgrehan char buf[VM_MAX_NAMELEN]; 447221828Sgrehan struct vmmdev_softc *sc; 448256651Sneel struct cdev *cdev; 449221828Sgrehan 450221828Sgrehan strlcpy(buf, "beavis", sizeof(buf)); 451221828Sgrehan error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 452221828Sgrehan if (error != 0 || req->newptr == NULL) 453221828Sgrehan return (error); 454221828Sgrehan 455221828Sgrehan mtx_lock(&vmmdev_mtx); 456221828Sgrehan sc = vmmdev_lookup(buf); 457256651Sneel if (sc == NULL || sc->cdev == NULL) { 458221828Sgrehan mtx_unlock(&vmmdev_mtx); 459221828Sgrehan return (EINVAL); 460221828Sgrehan } 461241454Sneel 462256651Sneel /* 463256651Sneel * The 'cdev' will be destroyed asynchronously when 'si_threadcount' 464256651Sneel * goes down to 0 so we should not do it again in the callback. 465256651Sneel */ 466256651Sneel cdev = sc->cdev; 467256651Sneel sc->cdev = NULL; 468221828Sgrehan mtx_unlock(&vmmdev_mtx); 469241454Sneel 470256651Sneel /* 471256651Sneel * Schedule the 'cdev' to be destroyed: 472256651Sneel * 473256651Sneel * - any new operations on this 'cdev' will return an error (ENXIO). 474256651Sneel * 475256651Sneel * - when the 'si_threadcount' dwindles down to zero the 'cdev' will 476256651Sneel * be destroyed and the callback will be invoked in a taskqueue 477256651Sneel * context. 478256651Sneel */ 479256651Sneel destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); 480241454Sneel 481221828Sgrehan return (0); 482221828Sgrehan} 483221828SgrehanSYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, CTLTYPE_STRING | CTLFLAG_RW, 484221828Sgrehan NULL, 0, sysctl_vmm_destroy, "A", NULL); 485221828Sgrehan 486221828Sgrehanstatic struct cdevsw vmmdevsw = { 487221828Sgrehan .d_name = "vmmdev", 488221828Sgrehan .d_version = D_VERSION, 489221828Sgrehan .d_ioctl = vmmdev_ioctl, 490256072Sneel .d_mmap_single = vmmdev_mmap_single, 491221828Sgrehan .d_read = vmmdev_rw, 492221828Sgrehan .d_write = vmmdev_rw, 493221828Sgrehan}; 494221828Sgrehan 495221828Sgrehanstatic int 496221828Sgrehansysctl_vmm_create(SYSCTL_HANDLER_ARGS) 497221828Sgrehan{ 498221828Sgrehan int error; 499221828Sgrehan struct vm *vm; 500256651Sneel struct cdev *cdev; 501241454Sneel struct vmmdev_softc *sc, *sc2; 502221828Sgrehan char buf[VM_MAX_NAMELEN]; 503221828Sgrehan 504221828Sgrehan strlcpy(buf, "beavis", sizeof(buf)); 505221828Sgrehan error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 506221828Sgrehan if (error != 0 || req->newptr == NULL) 507221828Sgrehan return (error); 508221828Sgrehan 509221828Sgrehan mtx_lock(&vmmdev_mtx); 510221828Sgrehan sc = vmmdev_lookup(buf); 511241454Sneel mtx_unlock(&vmmdev_mtx); 512241454Sneel if (sc != NULL) 513221828Sgrehan return (EEXIST); 514221828Sgrehan 515249396Sneel error = vm_create(buf, &vm); 516249396Sneel if (error != 0) 517249396Sneel return (error); 518221828Sgrehan 519221828Sgrehan sc = malloc(sizeof(struct vmmdev_softc), M_VMMDEV, M_WAITOK | M_ZERO); 520221828Sgrehan sc->vm = vm; 521241454Sneel 522241454Sneel /* 523241454Sneel * Lookup the name again just in case somebody sneaked in when we 524241454Sneel * dropped the lock. 525241454Sneel */ 526241454Sneel mtx_lock(&vmmdev_mtx); 527241454Sneel sc2 = vmmdev_lookup(buf); 528256651Sneel if (sc2 == NULL) { 529241454Sneel SLIST_INSERT_HEAD(&head, sc, link); 530256651Sneel sc->flags |= VSC_LINKED; 531256651Sneel } 532241454Sneel mtx_unlock(&vmmdev_mtx); 533241454Sneel 534241454Sneel if (sc2 != NULL) { 535256651Sneel vmmdev_destroy(sc); 536241454Sneel return (EEXIST); 537241454Sneel } 538241454Sneel 539256651Sneel error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, NULL, 540249435Sneel UID_ROOT, GID_WHEEL, 0600, "vmm/%s", buf); 541249435Sneel if (error != 0) { 542256651Sneel vmmdev_destroy(sc); 543249435Sneel return (error); 544249435Sneel } 545256651Sneel 546256651Sneel mtx_lock(&vmmdev_mtx); 547256651Sneel sc->cdev = cdev; 548221828Sgrehan sc->cdev->si_drv1 = sc; 549256651Sneel mtx_unlock(&vmmdev_mtx); 550221828Sgrehan 551221828Sgrehan return (0); 552221828Sgrehan} 553221828SgrehanSYSCTL_PROC(_hw_vmm, OID_AUTO, create, CTLTYPE_STRING | CTLFLAG_RW, 554221828Sgrehan NULL, 0, sysctl_vmm_create, "A", NULL); 555221828Sgrehan 556221828Sgrehanvoid 557221828Sgrehanvmmdev_init(void) 558221828Sgrehan{ 559221828Sgrehan mtx_init(&vmmdev_mtx, "vmm device mutex", NULL, MTX_DEF); 560221828Sgrehan} 561221828Sgrehan 562241454Sneelint 563221828Sgrehanvmmdev_cleanup(void) 564221828Sgrehan{ 565241454Sneel int error; 566221828Sgrehan 567241454Sneel if (SLIST_EMPTY(&head)) 568241454Sneel error = 0; 569241454Sneel else 570241454Sneel error = EBUSY; 571221828Sgrehan 572241454Sneel return (error); 573221828Sgrehan} 574