vmm_dev.c revision 268972
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 268972 2014-07-22 03:14:37Z jhb $ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/vmm_dev.c 268972 2014-07-22 03:14:37Z 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" 57268891Sjhb#include "io/vatpic.h" 58261088Sjhb#include "io/vioapic.h" 59261088Sjhb#include "io/vhpet.h" 60221828Sgrehan 61221828Sgrehanstruct vmmdev_softc { 62221828Sgrehan struct vm *vm; /* vm instance cookie */ 63221828Sgrehan struct cdev *cdev; 64221828Sgrehan SLIST_ENTRY(vmmdev_softc) link; 65256651Sneel int flags; 66221828Sgrehan}; 67256651Sneel#define VSC_LINKED 0x01 68256651Sneel 69221828Sgrehanstatic SLIST_HEAD(, vmmdev_softc) head; 70221828Sgrehan 71221828Sgrehanstatic struct mtx vmmdev_mtx; 72221828Sgrehan 73221828Sgrehanstatic MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev"); 74221828Sgrehan 75221828SgrehanSYSCTL_DECL(_hw_vmm); 76221828Sgrehan 77221828Sgrehanstatic struct vmmdev_softc * 78221828Sgrehanvmmdev_lookup(const char *name) 79221828Sgrehan{ 80221828Sgrehan struct vmmdev_softc *sc; 81221828Sgrehan 82221828Sgrehan#ifdef notyet /* XXX kernel is not compiled with invariants */ 83221828Sgrehan mtx_assert(&vmmdev_mtx, MA_OWNED); 84221828Sgrehan#endif 85221828Sgrehan 86221828Sgrehan SLIST_FOREACH(sc, &head, link) { 87221828Sgrehan if (strcmp(name, vm_name(sc->vm)) == 0) 88221828Sgrehan break; 89221828Sgrehan } 90221828Sgrehan 91221828Sgrehan return (sc); 92221828Sgrehan} 93221828Sgrehan 94221828Sgrehanstatic struct vmmdev_softc * 95221828Sgrehanvmmdev_lookup2(struct cdev *cdev) 96221828Sgrehan{ 97221828Sgrehan 98241454Sneel return (cdev->si_drv1); 99221828Sgrehan} 100221828Sgrehan 101221828Sgrehanstatic int 102221828Sgrehanvmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) 103221828Sgrehan{ 104256072Sneel int error, off, c, prot; 105256072Sneel vm_paddr_t gpa; 106256072Sneel void *hpa, *cookie; 107221828Sgrehan struct vmmdev_softc *sc; 108221828Sgrehan 109221828Sgrehan static char zerobuf[PAGE_SIZE]; 110221828Sgrehan 111221828Sgrehan error = 0; 112221828Sgrehan sc = vmmdev_lookup2(cdev); 113241454Sneel if (sc == NULL) 114241454Sneel error = ENXIO; 115221828Sgrehan 116256072Sneel prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ); 117221828Sgrehan while (uio->uio_resid > 0 && error == 0) { 118221828Sgrehan gpa = uio->uio_offset; 119221828Sgrehan off = gpa & PAGE_MASK; 120221828Sgrehan c = min(uio->uio_resid, PAGE_SIZE - off); 121221828Sgrehan 122221828Sgrehan /* 123221828Sgrehan * The VM has a hole in its physical memory map. If we want to 124221828Sgrehan * use 'dd' to inspect memory beyond the hole we need to 125221828Sgrehan * provide bogus data for memory that lies in the hole. 126221828Sgrehan * 127221828Sgrehan * Since this device does not support lseek(2), dd(1) will 128221828Sgrehan * read(2) blocks of data to simulate the lseek(2). 129221828Sgrehan */ 130256072Sneel hpa = vm_gpa_hold(sc->vm, gpa, c, prot, &cookie); 131256072Sneel if (hpa == NULL) { 132221828Sgrehan if (uio->uio_rw == UIO_READ) 133221828Sgrehan error = uiomove(zerobuf, c, uio); 134221828Sgrehan else 135221828Sgrehan error = EFAULT; 136256072Sneel } else { 137256072Sneel error = uiomove(hpa, c, uio); 138256072Sneel vm_gpa_release(cookie); 139256072Sneel } 140221828Sgrehan } 141221828Sgrehan return (error); 142221828Sgrehan} 143221828Sgrehan 144221828Sgrehanstatic int 145221828Sgrehanvmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 146221828Sgrehan struct thread *td) 147221828Sgrehan{ 148241489Sneel int error, vcpu, state_changed; 149221828Sgrehan struct vmmdev_softc *sc; 150221828Sgrehan struct vm_memory_segment *seg; 151221828Sgrehan struct vm_register *vmreg; 152261088Sjhb struct vm_seg_desc *vmsegdesc; 153221828Sgrehan struct vm_run *vmrun; 154267427Sjhb struct vm_exception *vmexc; 155221828Sgrehan struct vm_lapic_irq *vmirq; 156262350Sjhb struct vm_lapic_msi *vmmsi; 157261088Sjhb struct vm_ioapic_irq *ioapic_irq; 158268891Sjhb struct vm_isa_irq *isa_irq; 159268972Sjhb struct vm_isa_irq_trigger *isa_irq_trigger; 160221828Sgrehan struct vm_capability *vmcap; 161221828Sgrehan struct vm_pptdev *pptdev; 162221828Sgrehan struct vm_pptdev_mmio *pptmmio; 163221828Sgrehan struct vm_pptdev_msi *pptmsi; 164234761Sgrehan struct vm_pptdev_msix *pptmsix; 165221828Sgrehan struct vm_nmi *vmnmi; 166221828Sgrehan struct vm_stats *vmstats; 167221828Sgrehan struct vm_stat_desc *statdesc; 168240922Sneel struct vm_x2apic *x2apic; 169256072Sneel struct vm_gpa_pte *gpapte; 170268935Sjhb struct vm_suspend *vmsuspend; 171221828Sgrehan 172221828Sgrehan sc = vmmdev_lookup2(cdev); 173241489Sneel if (sc == NULL) 174221828Sgrehan return (ENXIO); 175221828Sgrehan 176268891Sjhb error = 0; 177241489Sneel vcpu = -1; 178241489Sneel state_changed = 0; 179241489Sneel 180221828Sgrehan /* 181221828Sgrehan * Some VMM ioctls can operate only on vcpus that are not running. 182221828Sgrehan */ 183221828Sgrehan switch (cmd) { 184221828Sgrehan case VM_RUN: 185221828Sgrehan case VM_GET_REGISTER: 186221828Sgrehan case VM_SET_REGISTER: 187221828Sgrehan case VM_GET_SEGMENT_DESCRIPTOR: 188221828Sgrehan case VM_SET_SEGMENT_DESCRIPTOR: 189267427Sjhb case VM_INJECT_EXCEPTION: 190221828Sgrehan case VM_GET_CAPABILITY: 191221828Sgrehan case VM_SET_CAPABILITY: 192221828Sgrehan case VM_PPTDEV_MSI: 193241489Sneel case VM_PPTDEV_MSIX: 194240922Sneel case VM_SET_X2APIC_STATE: 195221828Sgrehan /* 196221828Sgrehan * XXX fragile, handle with care 197221828Sgrehan * Assumes that the first field of the ioctl data is the vcpu. 198221828Sgrehan */ 199221828Sgrehan vcpu = *(int *)data; 200221828Sgrehan if (vcpu < 0 || vcpu >= VM_MAXCPU) { 201221828Sgrehan error = EINVAL; 202221828Sgrehan goto done; 203221828Sgrehan } 204221828Sgrehan 205266393Sjhb error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); 206241489Sneel if (error) 207221828Sgrehan goto done; 208241489Sneel 209241489Sneel state_changed = 1; 210241489Sneel break; 211241489Sneel 212241489Sneel case VM_MAP_PPTDEV_MMIO: 213241489Sneel case VM_BIND_PPTDEV: 214241489Sneel case VM_UNBIND_PPTDEV: 215241489Sneel case VM_MAP_MEMORY: 216241489Sneel /* 217241489Sneel * ioctls that operate on the entire virtual machine must 218241489Sneel * prevent all vcpus from running. 219241489Sneel */ 220241489Sneel error = 0; 221241489Sneel for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) { 222266393Sjhb error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); 223241489Sneel if (error) 224241489Sneel break; 225221828Sgrehan } 226241489Sneel 227241489Sneel if (error) { 228241489Sneel while (--vcpu >= 0) 229266393Sjhb vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); 230241489Sneel goto done; 231241489Sneel } 232241489Sneel 233241489Sneel state_changed = 2; 234221828Sgrehan break; 235241489Sneel 236221828Sgrehan default: 237221828Sgrehan break; 238221828Sgrehan } 239221828Sgrehan 240221828Sgrehan switch(cmd) { 241221828Sgrehan case VM_RUN: 242221828Sgrehan vmrun = (struct vm_run *)data; 243221828Sgrehan error = vm_run(sc->vm, vmrun); 244221828Sgrehan break; 245268935Sjhb case VM_SUSPEND: 246268935Sjhb vmsuspend = (struct vm_suspend *)data; 247268935Sjhb error = vm_suspend(sc->vm, vmsuspend->how); 248268935Sjhb break; 249221828Sgrehan case VM_STAT_DESC: { 250221828Sgrehan statdesc = (struct vm_stat_desc *)data; 251250427Sneel error = vmm_stat_desc_copy(statdesc->index, 252250427Sneel statdesc->desc, sizeof(statdesc->desc)); 253221828Sgrehan break; 254221828Sgrehan } 255221828Sgrehan case VM_STATS: { 256250427Sneel CTASSERT(MAX_VM_STATS >= MAX_VMM_STAT_ELEMS); 257221828Sgrehan vmstats = (struct vm_stats *)data; 258221828Sgrehan getmicrotime(&vmstats->tv); 259221828Sgrehan error = vmm_stat_copy(sc->vm, vmstats->cpuid, 260221828Sgrehan &vmstats->num_entries, vmstats->statbuf); 261221828Sgrehan break; 262221828Sgrehan } 263221828Sgrehan case VM_PPTDEV_MSI: 264221828Sgrehan pptmsi = (struct vm_pptdev_msi *)data; 265221828Sgrehan error = ppt_setup_msi(sc->vm, pptmsi->vcpu, 266221828Sgrehan pptmsi->bus, pptmsi->slot, pptmsi->func, 267262350Sjhb pptmsi->addr, pptmsi->msg, 268221828Sgrehan pptmsi->numvec); 269221828Sgrehan break; 270234761Sgrehan case VM_PPTDEV_MSIX: 271234761Sgrehan pptmsix = (struct vm_pptdev_msix *)data; 272234761Sgrehan error = ppt_setup_msix(sc->vm, pptmsix->vcpu, 273234761Sgrehan pptmsix->bus, pptmsix->slot, 274234761Sgrehan pptmsix->func, pptmsix->idx, 275262350Sjhb pptmsix->addr, pptmsix->msg, 276262350Sjhb pptmsix->vector_control); 277234761Sgrehan break; 278221828Sgrehan case VM_MAP_PPTDEV_MMIO: 279221828Sgrehan pptmmio = (struct vm_pptdev_mmio *)data; 280221828Sgrehan error = ppt_map_mmio(sc->vm, pptmmio->bus, pptmmio->slot, 281221828Sgrehan pptmmio->func, pptmmio->gpa, pptmmio->len, 282221828Sgrehan pptmmio->hpa); 283221828Sgrehan break; 284221828Sgrehan case VM_BIND_PPTDEV: 285221828Sgrehan pptdev = (struct vm_pptdev *)data; 286256072Sneel error = vm_assign_pptdev(sc->vm, pptdev->bus, pptdev->slot, 287256072Sneel pptdev->func); 288221828Sgrehan break; 289221828Sgrehan case VM_UNBIND_PPTDEV: 290221828Sgrehan pptdev = (struct vm_pptdev *)data; 291256072Sneel error = vm_unassign_pptdev(sc->vm, pptdev->bus, pptdev->slot, 292256072Sneel pptdev->func); 293221828Sgrehan break; 294267427Sjhb case VM_INJECT_EXCEPTION: 295267427Sjhb vmexc = (struct vm_exception *)data; 296267427Sjhb error = vm_inject_exception(sc->vm, vmexc->cpuid, vmexc); 297221828Sgrehan break; 298221828Sgrehan case VM_INJECT_NMI: 299221828Sgrehan vmnmi = (struct vm_nmi *)data; 300221828Sgrehan error = vm_inject_nmi(sc->vm, vmnmi->cpuid); 301221828Sgrehan break; 302221828Sgrehan case VM_LAPIC_IRQ: 303221828Sgrehan vmirq = (struct vm_lapic_irq *)data; 304261088Sjhb error = lapic_intr_edge(sc->vm, vmirq->cpuid, vmirq->vector); 305221828Sgrehan break; 306262350Sjhb case VM_LAPIC_LOCAL_IRQ: 307262350Sjhb vmirq = (struct vm_lapic_irq *)data; 308262350Sjhb error = lapic_set_local_intr(sc->vm, vmirq->cpuid, 309262350Sjhb vmirq->vector); 310262350Sjhb break; 311262350Sjhb case VM_LAPIC_MSI: 312262350Sjhb vmmsi = (struct vm_lapic_msi *)data; 313262350Sjhb error = lapic_intr_msi(sc->vm, vmmsi->addr, vmmsi->msg); 314262350Sjhb break; 315261088Sjhb case VM_IOAPIC_ASSERT_IRQ: 316261088Sjhb ioapic_irq = (struct vm_ioapic_irq *)data; 317261088Sjhb error = vioapic_assert_irq(sc->vm, ioapic_irq->irq); 318261088Sjhb break; 319261088Sjhb case VM_IOAPIC_DEASSERT_IRQ: 320261088Sjhb ioapic_irq = (struct vm_ioapic_irq *)data; 321261088Sjhb error = vioapic_deassert_irq(sc->vm, ioapic_irq->irq); 322261088Sjhb break; 323261088Sjhb case VM_IOAPIC_PULSE_IRQ: 324261088Sjhb ioapic_irq = (struct vm_ioapic_irq *)data; 325261088Sjhb error = vioapic_pulse_irq(sc->vm, ioapic_irq->irq); 326261088Sjhb break; 327267393Sjhb case VM_IOAPIC_PINCOUNT: 328267393Sjhb *(int *)data = vioapic_pincount(sc->vm); 329267393Sjhb break; 330268891Sjhb case VM_ISA_ASSERT_IRQ: 331268891Sjhb isa_irq = (struct vm_isa_irq *)data; 332268891Sjhb error = vatpic_assert_irq(sc->vm, isa_irq->atpic_irq); 333268891Sjhb if (error == 0 && isa_irq->ioapic_irq != -1) 334268891Sjhb error = vioapic_assert_irq(sc->vm, 335268891Sjhb isa_irq->ioapic_irq); 336268891Sjhb break; 337268891Sjhb case VM_ISA_DEASSERT_IRQ: 338268891Sjhb isa_irq = (struct vm_isa_irq *)data; 339268891Sjhb error = vatpic_deassert_irq(sc->vm, isa_irq->atpic_irq); 340268891Sjhb if (error == 0 && isa_irq->ioapic_irq != -1) 341268891Sjhb error = vioapic_deassert_irq(sc->vm, 342268891Sjhb isa_irq->ioapic_irq); 343268891Sjhb break; 344268891Sjhb case VM_ISA_PULSE_IRQ: 345268891Sjhb isa_irq = (struct vm_isa_irq *)data; 346268891Sjhb error = vatpic_pulse_irq(sc->vm, isa_irq->atpic_irq); 347268891Sjhb if (error == 0 && isa_irq->ioapic_irq != -1) 348268891Sjhb error = vioapic_pulse_irq(sc->vm, isa_irq->ioapic_irq); 349268891Sjhb break; 350268972Sjhb case VM_ISA_SET_IRQ_TRIGGER: 351268972Sjhb isa_irq_trigger = (struct vm_isa_irq_trigger *)data; 352268972Sjhb error = vatpic_set_irq_trigger(sc->vm, 353268972Sjhb isa_irq_trigger->atpic_irq, isa_irq_trigger->trigger); 354268972Sjhb break; 355221828Sgrehan case VM_MAP_MEMORY: 356221828Sgrehan seg = (struct vm_memory_segment *)data; 357241041Sneel error = vm_malloc(sc->vm, seg->gpa, seg->len); 358221828Sgrehan break; 359221828Sgrehan case VM_GET_MEMORY_SEG: 360221828Sgrehan seg = (struct vm_memory_segment *)data; 361241178Sneel seg->len = 0; 362221828Sgrehan (void)vm_gpabase2memseg(sc->vm, seg->gpa, seg); 363221828Sgrehan error = 0; 364221828Sgrehan break; 365221828Sgrehan case VM_GET_REGISTER: 366221828Sgrehan vmreg = (struct vm_register *)data; 367221828Sgrehan error = vm_get_register(sc->vm, vmreg->cpuid, vmreg->regnum, 368221828Sgrehan &vmreg->regval); 369221828Sgrehan break; 370221828Sgrehan case VM_SET_REGISTER: 371221828Sgrehan vmreg = (struct vm_register *)data; 372221828Sgrehan error = vm_set_register(sc->vm, vmreg->cpuid, vmreg->regnum, 373221828Sgrehan vmreg->regval); 374221828Sgrehan break; 375221828Sgrehan case VM_SET_SEGMENT_DESCRIPTOR: 376221828Sgrehan vmsegdesc = (struct vm_seg_desc *)data; 377221828Sgrehan error = vm_set_seg_desc(sc->vm, vmsegdesc->cpuid, 378221828Sgrehan vmsegdesc->regnum, 379221828Sgrehan &vmsegdesc->desc); 380221828Sgrehan break; 381221828Sgrehan case VM_GET_SEGMENT_DESCRIPTOR: 382221828Sgrehan vmsegdesc = (struct vm_seg_desc *)data; 383221828Sgrehan error = vm_get_seg_desc(sc->vm, vmsegdesc->cpuid, 384221828Sgrehan vmsegdesc->regnum, 385221828Sgrehan &vmsegdesc->desc); 386221828Sgrehan break; 387221828Sgrehan case VM_GET_CAPABILITY: 388221828Sgrehan vmcap = (struct vm_capability *)data; 389221828Sgrehan error = vm_get_capability(sc->vm, vmcap->cpuid, 390221828Sgrehan vmcap->captype, 391221828Sgrehan &vmcap->capval); 392221828Sgrehan break; 393221828Sgrehan case VM_SET_CAPABILITY: 394221828Sgrehan vmcap = (struct vm_capability *)data; 395221828Sgrehan error = vm_set_capability(sc->vm, vmcap->cpuid, 396221828Sgrehan vmcap->captype, 397221828Sgrehan vmcap->capval); 398221828Sgrehan break; 399240922Sneel case VM_SET_X2APIC_STATE: 400240922Sneel x2apic = (struct vm_x2apic *)data; 401240922Sneel error = vm_set_x2apic_state(sc->vm, 402240922Sneel x2apic->cpuid, x2apic->state); 403240922Sneel break; 404240922Sneel case VM_GET_X2APIC_STATE: 405240922Sneel x2apic = (struct vm_x2apic *)data; 406240922Sneel error = vm_get_x2apic_state(sc->vm, 407240922Sneel x2apic->cpuid, &x2apic->state); 408240922Sneel break; 409256072Sneel case VM_GET_GPA_PMAP: 410256072Sneel gpapte = (struct vm_gpa_pte *)data; 411256072Sneel pmap_get_mapping(vmspace_pmap(vm_get_vmspace(sc->vm)), 412256072Sneel gpapte->gpa, gpapte->pte, &gpapte->ptenum); 413256072Sneel error = 0; 414256072Sneel break; 415261088Sjhb case VM_GET_HPET_CAPABILITIES: 416261088Sjhb error = vhpet_getcap((struct vm_hpet_cap *)data); 417261088Sjhb break; 418221828Sgrehan default: 419221828Sgrehan error = ENOTTY; 420221828Sgrehan break; 421221828Sgrehan } 422241489Sneel 423241489Sneel if (state_changed == 1) { 424266393Sjhb vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); 425241489Sneel } else if (state_changed == 2) { 426241489Sneel for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) 427266393Sjhb vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); 428241489Sneel } 429241489Sneel 430221828Sgrehandone: 431256072Sneel /* Make sure that no handler returns a bogus value like ERESTART */ 432256072Sneel KASSERT(error >= 0, ("vmmdev_ioctl: invalid error return %d", error)); 433221828Sgrehan return (error); 434221828Sgrehan} 435221828Sgrehan 436221828Sgrehanstatic int 437256072Sneelvmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, 438256072Sneel vm_size_t size, struct vm_object **object, int nprot) 439221828Sgrehan{ 440221828Sgrehan int error; 441221828Sgrehan struct vmmdev_softc *sc; 442221828Sgrehan 443221828Sgrehan sc = vmmdev_lookup2(cdev); 444256072Sneel if (sc != NULL && (nprot & PROT_EXEC) == 0) 445256072Sneel error = vm_get_memobj(sc->vm, *offset, size, offset, object); 446256072Sneel else 447256072Sneel error = EINVAL; 448221828Sgrehan 449221828Sgrehan return (error); 450221828Sgrehan} 451221828Sgrehan 452221828Sgrehanstatic void 453256651Sneelvmmdev_destroy(void *arg) 454221828Sgrehan{ 455221828Sgrehan 456256651Sneel struct vmmdev_softc *sc = arg; 457256651Sneel 458256651Sneel if (sc->cdev != NULL) 459241454Sneel destroy_dev(sc->cdev); 460241454Sneel 461256651Sneel if (sc->vm != NULL) 462241454Sneel vm_destroy(sc->vm); 463241454Sneel 464256651Sneel if ((sc->flags & VSC_LINKED) != 0) { 465241454Sneel mtx_lock(&vmmdev_mtx); 466241454Sneel SLIST_REMOVE(&head, sc, vmmdev_softc, link); 467241454Sneel mtx_unlock(&vmmdev_mtx); 468241454Sneel } 469241454Sneel 470221828Sgrehan free(sc, M_VMMDEV); 471221828Sgrehan} 472221828Sgrehan 473221828Sgrehanstatic int 474221828Sgrehansysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) 475221828Sgrehan{ 476221828Sgrehan int error; 477221828Sgrehan char buf[VM_MAX_NAMELEN]; 478221828Sgrehan struct vmmdev_softc *sc; 479256651Sneel struct cdev *cdev; 480221828Sgrehan 481221828Sgrehan strlcpy(buf, "beavis", sizeof(buf)); 482221828Sgrehan error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 483221828Sgrehan if (error != 0 || req->newptr == NULL) 484221828Sgrehan return (error); 485221828Sgrehan 486221828Sgrehan mtx_lock(&vmmdev_mtx); 487221828Sgrehan sc = vmmdev_lookup(buf); 488256651Sneel if (sc == NULL || sc->cdev == NULL) { 489221828Sgrehan mtx_unlock(&vmmdev_mtx); 490221828Sgrehan return (EINVAL); 491221828Sgrehan } 492241454Sneel 493256651Sneel /* 494256651Sneel * The 'cdev' will be destroyed asynchronously when 'si_threadcount' 495256651Sneel * goes down to 0 so we should not do it again in the callback. 496256651Sneel */ 497256651Sneel cdev = sc->cdev; 498256651Sneel sc->cdev = NULL; 499221828Sgrehan mtx_unlock(&vmmdev_mtx); 500241454Sneel 501256651Sneel /* 502256651Sneel * Schedule the 'cdev' to be destroyed: 503256651Sneel * 504256651Sneel * - any new operations on this 'cdev' will return an error (ENXIO). 505256651Sneel * 506256651Sneel * - when the 'si_threadcount' dwindles down to zero the 'cdev' will 507256651Sneel * be destroyed and the callback will be invoked in a taskqueue 508256651Sneel * context. 509256651Sneel */ 510256651Sneel destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); 511241454Sneel 512221828Sgrehan return (0); 513221828Sgrehan} 514221828SgrehanSYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, CTLTYPE_STRING | CTLFLAG_RW, 515221828Sgrehan NULL, 0, sysctl_vmm_destroy, "A", NULL); 516221828Sgrehan 517221828Sgrehanstatic struct cdevsw vmmdevsw = { 518221828Sgrehan .d_name = "vmmdev", 519221828Sgrehan .d_version = D_VERSION, 520221828Sgrehan .d_ioctl = vmmdev_ioctl, 521256072Sneel .d_mmap_single = vmmdev_mmap_single, 522221828Sgrehan .d_read = vmmdev_rw, 523221828Sgrehan .d_write = vmmdev_rw, 524221828Sgrehan}; 525221828Sgrehan 526221828Sgrehanstatic int 527221828Sgrehansysctl_vmm_create(SYSCTL_HANDLER_ARGS) 528221828Sgrehan{ 529221828Sgrehan int error; 530221828Sgrehan struct vm *vm; 531256651Sneel struct cdev *cdev; 532241454Sneel struct vmmdev_softc *sc, *sc2; 533221828Sgrehan char buf[VM_MAX_NAMELEN]; 534221828Sgrehan 535221828Sgrehan strlcpy(buf, "beavis", sizeof(buf)); 536221828Sgrehan error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 537221828Sgrehan if (error != 0 || req->newptr == NULL) 538221828Sgrehan return (error); 539221828Sgrehan 540221828Sgrehan mtx_lock(&vmmdev_mtx); 541221828Sgrehan sc = vmmdev_lookup(buf); 542241454Sneel mtx_unlock(&vmmdev_mtx); 543241454Sneel if (sc != NULL) 544221828Sgrehan return (EEXIST); 545221828Sgrehan 546249396Sneel error = vm_create(buf, &vm); 547249396Sneel if (error != 0) 548249396Sneel return (error); 549221828Sgrehan 550221828Sgrehan sc = malloc(sizeof(struct vmmdev_softc), M_VMMDEV, M_WAITOK | M_ZERO); 551221828Sgrehan sc->vm = vm; 552241454Sneel 553241454Sneel /* 554241454Sneel * Lookup the name again just in case somebody sneaked in when we 555241454Sneel * dropped the lock. 556241454Sneel */ 557241454Sneel mtx_lock(&vmmdev_mtx); 558241454Sneel sc2 = vmmdev_lookup(buf); 559256651Sneel if (sc2 == NULL) { 560241454Sneel SLIST_INSERT_HEAD(&head, sc, link); 561256651Sneel sc->flags |= VSC_LINKED; 562256651Sneel } 563241454Sneel mtx_unlock(&vmmdev_mtx); 564241454Sneel 565241454Sneel if (sc2 != NULL) { 566256651Sneel vmmdev_destroy(sc); 567241454Sneel return (EEXIST); 568241454Sneel } 569241454Sneel 570256651Sneel error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, NULL, 571249435Sneel UID_ROOT, GID_WHEEL, 0600, "vmm/%s", buf); 572249435Sneel if (error != 0) { 573256651Sneel vmmdev_destroy(sc); 574249435Sneel return (error); 575249435Sneel } 576256651Sneel 577256651Sneel mtx_lock(&vmmdev_mtx); 578256651Sneel sc->cdev = cdev; 579221828Sgrehan sc->cdev->si_drv1 = sc; 580256651Sneel mtx_unlock(&vmmdev_mtx); 581221828Sgrehan 582221828Sgrehan return (0); 583221828Sgrehan} 584221828SgrehanSYSCTL_PROC(_hw_vmm, OID_AUTO, create, CTLTYPE_STRING | CTLFLAG_RW, 585221828Sgrehan NULL, 0, sysctl_vmm_create, "A", NULL); 586221828Sgrehan 587221828Sgrehanvoid 588221828Sgrehanvmmdev_init(void) 589221828Sgrehan{ 590221828Sgrehan mtx_init(&vmmdev_mtx, "vmm device mutex", NULL, MTX_DEF); 591221828Sgrehan} 592221828Sgrehan 593241454Sneelint 594221828Sgrehanvmmdev_cleanup(void) 595221828Sgrehan{ 596241454Sneel int error; 597221828Sgrehan 598241454Sneel if (SLIST_EMPTY(&head)) 599241454Sneel error = 0; 600241454Sneel else 601241454Sneel error = EBUSY; 602221828Sgrehan 603241454Sneel return (error); 604221828Sgrehan} 605