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 312990 2017-01-30 14:34:04Z avg $ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/vmm_dev.c 312990 2017-01-30 14:34:04Z avg $"); 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> 48295124Sgrehan#include <vm/vm_object.h> 49221828Sgrehan 50221828Sgrehan#include <machine/vmparam.h> 51261088Sjhb#include <machine/vmm.h> 52268976Sjhb#include <machine/vmm_instruction_emul.h> 53261088Sjhb#include <machine/vmm_dev.h> 54221828Sgrehan 55221828Sgrehan#include "vmm_lapic.h" 56221828Sgrehan#include "vmm_stat.h" 57239700Sgrehan#include "vmm_mem.h" 58221828Sgrehan#include "io/ppt.h" 59268891Sjhb#include "io/vatpic.h" 60261088Sjhb#include "io/vioapic.h" 61261088Sjhb#include "io/vhpet.h" 62284894Sneel#include "io/vrtc.h" 63221828Sgrehan 64295124Sgrehanstruct devmem_softc { 65295124Sgrehan int segid; 66295124Sgrehan char *name; 67295124Sgrehan struct cdev *cdev; 68295124Sgrehan struct vmmdev_softc *sc; 69295124Sgrehan SLIST_ENTRY(devmem_softc) link; 70295124Sgrehan}; 71295124Sgrehan 72221828Sgrehanstruct vmmdev_softc { 73221828Sgrehan struct vm *vm; /* vm instance cookie */ 74221828Sgrehan struct cdev *cdev; 75221828Sgrehan SLIST_ENTRY(vmmdev_softc) link; 76295124Sgrehan SLIST_HEAD(, devmem_softc) devmem; 77256651Sneel int flags; 78221828Sgrehan}; 79256651Sneel#define VSC_LINKED 0x01 80256651Sneel 81221828Sgrehanstatic SLIST_HEAD(, vmmdev_softc) head; 82221828Sgrehan 83221828Sgrehanstatic struct mtx vmmdev_mtx; 84221828Sgrehan 85221828Sgrehanstatic MALLOC_DEFINE(M_VMMDEV, "vmmdev", "vmmdev"); 86221828Sgrehan 87221828SgrehanSYSCTL_DECL(_hw_vmm); 88221828Sgrehan 89295124Sgrehanstatic int devmem_create_cdev(const char *vmname, int id, char *devmem); 90295124Sgrehanstatic void devmem_destroy(void *arg); 91295124Sgrehan 92295124Sgrehanstatic int 93295124Sgrehanvcpu_lock_one(struct vmmdev_softc *sc, int vcpu) 94295124Sgrehan{ 95295124Sgrehan int error; 96295124Sgrehan 97295124Sgrehan if (vcpu < 0 || vcpu >= VM_MAXCPU) 98295124Sgrehan return (EINVAL); 99295124Sgrehan 100295124Sgrehan error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); 101295124Sgrehan return (error); 102295124Sgrehan} 103295124Sgrehan 104295124Sgrehanstatic void 105295124Sgrehanvcpu_unlock_one(struct vmmdev_softc *sc, int vcpu) 106295124Sgrehan{ 107295124Sgrehan enum vcpu_state state; 108295124Sgrehan 109295124Sgrehan state = vcpu_get_state(sc->vm, vcpu, NULL); 110295124Sgrehan if (state != VCPU_FROZEN) { 111295124Sgrehan panic("vcpu %s(%d) has invalid state %d", vm_name(sc->vm), 112295124Sgrehan vcpu, state); 113295124Sgrehan } 114295124Sgrehan 115295124Sgrehan vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); 116295124Sgrehan} 117295124Sgrehan 118295124Sgrehanstatic int 119295124Sgrehanvcpu_lock_all(struct vmmdev_softc *sc) 120295124Sgrehan{ 121295124Sgrehan int error, vcpu; 122295124Sgrehan 123295124Sgrehan for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) { 124295124Sgrehan error = vcpu_lock_one(sc, vcpu); 125295124Sgrehan if (error) 126295124Sgrehan break; 127295124Sgrehan } 128295124Sgrehan 129295124Sgrehan if (error) { 130295124Sgrehan while (--vcpu >= 0) 131295124Sgrehan vcpu_unlock_one(sc, vcpu); 132295124Sgrehan } 133295124Sgrehan 134295124Sgrehan return (error); 135295124Sgrehan} 136295124Sgrehan 137295124Sgrehanstatic void 138295124Sgrehanvcpu_unlock_all(struct vmmdev_softc *sc) 139295124Sgrehan{ 140295124Sgrehan int vcpu; 141295124Sgrehan 142295124Sgrehan for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) 143295124Sgrehan vcpu_unlock_one(sc, vcpu); 144295124Sgrehan} 145295124Sgrehan 146221828Sgrehanstatic struct vmmdev_softc * 147221828Sgrehanvmmdev_lookup(const char *name) 148221828Sgrehan{ 149221828Sgrehan struct vmmdev_softc *sc; 150221828Sgrehan 151221828Sgrehan#ifdef notyet /* XXX kernel is not compiled with invariants */ 152221828Sgrehan mtx_assert(&vmmdev_mtx, MA_OWNED); 153221828Sgrehan#endif 154221828Sgrehan 155221828Sgrehan SLIST_FOREACH(sc, &head, link) { 156221828Sgrehan if (strcmp(name, vm_name(sc->vm)) == 0) 157221828Sgrehan break; 158221828Sgrehan } 159221828Sgrehan 160221828Sgrehan return (sc); 161221828Sgrehan} 162221828Sgrehan 163221828Sgrehanstatic struct vmmdev_softc * 164221828Sgrehanvmmdev_lookup2(struct cdev *cdev) 165221828Sgrehan{ 166221828Sgrehan 167241454Sneel return (cdev->si_drv1); 168221828Sgrehan} 169221828Sgrehan 170221828Sgrehanstatic int 171221828Sgrehanvmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) 172221828Sgrehan{ 173256072Sneel int error, off, c, prot; 174256072Sneel vm_paddr_t gpa; 175256072Sneel void *hpa, *cookie; 176221828Sgrehan struct vmmdev_softc *sc; 177221828Sgrehan 178221828Sgrehan sc = vmmdev_lookup2(cdev); 179241454Sneel if (sc == NULL) 180295124Sgrehan return (ENXIO); 181221828Sgrehan 182295124Sgrehan /* 183295124Sgrehan * Get a read lock on the guest memory map by freezing any vcpu. 184295124Sgrehan */ 185295124Sgrehan error = vcpu_lock_one(sc, VM_MAXCPU - 1); 186295124Sgrehan if (error) 187295124Sgrehan return (error); 188295124Sgrehan 189256072Sneel prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ); 190221828Sgrehan while (uio->uio_resid > 0 && error == 0) { 191221828Sgrehan gpa = uio->uio_offset; 192221828Sgrehan off = gpa & PAGE_MASK; 193221828Sgrehan c = min(uio->uio_resid, PAGE_SIZE - off); 194221828Sgrehan 195221828Sgrehan /* 196221828Sgrehan * The VM has a hole in its physical memory map. If we want to 197221828Sgrehan * use 'dd' to inspect memory beyond the hole we need to 198221828Sgrehan * provide bogus data for memory that lies in the hole. 199221828Sgrehan * 200221828Sgrehan * Since this device does not support lseek(2), dd(1) will 201221828Sgrehan * read(2) blocks of data to simulate the lseek(2). 202221828Sgrehan */ 203295124Sgrehan hpa = vm_gpa_hold(sc->vm, VM_MAXCPU - 1, gpa, c, prot, &cookie); 204256072Sneel if (hpa == NULL) { 205221828Sgrehan if (uio->uio_rw == UIO_READ) 206295124Sgrehan error = uiomove(__DECONST(void *, zero_region), 207295124Sgrehan c, uio); 208221828Sgrehan else 209221828Sgrehan error = EFAULT; 210256072Sneel } else { 211256072Sneel error = uiomove(hpa, c, uio); 212256072Sneel vm_gpa_release(cookie); 213256072Sneel } 214221828Sgrehan } 215295124Sgrehan vcpu_unlock_one(sc, VM_MAXCPU - 1); 216221828Sgrehan return (error); 217221828Sgrehan} 218221828Sgrehan 219295124SgrehanCTASSERT(sizeof(((struct vm_memseg *)0)->name) >= SPECNAMELEN + 1); 220295124Sgrehan 221221828Sgrehanstatic int 222295124Sgrehanget_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg) 223295124Sgrehan{ 224295124Sgrehan struct devmem_softc *dsc; 225295124Sgrehan int error; 226295124Sgrehan bool sysmem; 227295124Sgrehan 228295124Sgrehan error = vm_get_memseg(sc->vm, mseg->segid, &mseg->len, &sysmem, NULL); 229295124Sgrehan if (error || mseg->len == 0) 230295124Sgrehan return (error); 231295124Sgrehan 232295124Sgrehan if (!sysmem) { 233295124Sgrehan SLIST_FOREACH(dsc, &sc->devmem, link) { 234295124Sgrehan if (dsc->segid == mseg->segid) 235295124Sgrehan break; 236295124Sgrehan } 237295124Sgrehan KASSERT(dsc != NULL, ("%s: devmem segment %d not found", 238295124Sgrehan __func__, mseg->segid)); 239295124Sgrehan error = copystr(dsc->name, mseg->name, SPECNAMELEN + 1, NULL); 240295124Sgrehan } else { 241295124Sgrehan bzero(mseg->name, sizeof(mseg->name)); 242295124Sgrehan } 243295124Sgrehan 244295124Sgrehan return (error); 245295124Sgrehan} 246295124Sgrehan 247295124Sgrehanstatic int 248295124Sgrehanalloc_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg) 249295124Sgrehan{ 250295124Sgrehan char *name; 251295124Sgrehan int error; 252295124Sgrehan bool sysmem; 253295124Sgrehan 254295124Sgrehan error = 0; 255295124Sgrehan name = NULL; 256295124Sgrehan sysmem = true; 257295124Sgrehan 258295124Sgrehan if (VM_MEMSEG_NAME(mseg)) { 259295124Sgrehan sysmem = false; 260295124Sgrehan name = malloc(SPECNAMELEN + 1, M_VMMDEV, M_WAITOK); 261312990Savg error = copystr(mseg->name, name, SPECNAMELEN + 1, 0); 262295124Sgrehan if (error) 263295124Sgrehan goto done; 264295124Sgrehan } 265295124Sgrehan 266295124Sgrehan error = vm_alloc_memseg(sc->vm, mseg->segid, mseg->len, sysmem); 267295124Sgrehan if (error) 268295124Sgrehan goto done; 269295124Sgrehan 270295124Sgrehan if (VM_MEMSEG_NAME(mseg)) { 271295124Sgrehan error = devmem_create_cdev(vm_name(sc->vm), mseg->segid, name); 272295124Sgrehan if (error) 273295124Sgrehan vm_free_memseg(sc->vm, mseg->segid); 274295124Sgrehan else 275295124Sgrehan name = NULL; /* freed when 'cdev' is destroyed */ 276295124Sgrehan } 277295124Sgrehandone: 278295124Sgrehan free(name, M_VMMDEV); 279295124Sgrehan return (error); 280295124Sgrehan} 281295124Sgrehan 282295124Sgrehanstatic int 283221828Sgrehanvmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, 284221828Sgrehan struct thread *td) 285221828Sgrehan{ 286270070Sgrehan int error, vcpu, state_changed, size; 287270070Sgrehan cpuset_t *cpuset; 288221828Sgrehan struct vmmdev_softc *sc; 289221828Sgrehan struct vm_register *vmreg; 290261088Sjhb struct vm_seg_desc *vmsegdesc; 291221828Sgrehan struct vm_run *vmrun; 292267427Sjhb struct vm_exception *vmexc; 293221828Sgrehan struct vm_lapic_irq *vmirq; 294262350Sjhb struct vm_lapic_msi *vmmsi; 295261088Sjhb struct vm_ioapic_irq *ioapic_irq; 296268891Sjhb struct vm_isa_irq *isa_irq; 297268972Sjhb struct vm_isa_irq_trigger *isa_irq_trigger; 298221828Sgrehan struct vm_capability *vmcap; 299221828Sgrehan struct vm_pptdev *pptdev; 300221828Sgrehan struct vm_pptdev_mmio *pptmmio; 301221828Sgrehan struct vm_pptdev_msi *pptmsi; 302234761Sgrehan struct vm_pptdev_msix *pptmsix; 303221828Sgrehan struct vm_nmi *vmnmi; 304221828Sgrehan struct vm_stats *vmstats; 305221828Sgrehan struct vm_stat_desc *statdesc; 306240922Sneel struct vm_x2apic *x2apic; 307256072Sneel struct vm_gpa_pte *gpapte; 308268935Sjhb struct vm_suspend *vmsuspend; 309268976Sjhb struct vm_gla2gpa *gg; 310270070Sgrehan struct vm_activate_cpu *vac; 311270070Sgrehan struct vm_cpuset *vm_cpuset; 312270159Sgrehan struct vm_intinfo *vmii; 313284894Sneel struct vm_rtc_time *rtctime; 314284894Sneel struct vm_rtc_data *rtcdata; 315295124Sgrehan struct vm_memmap *mm; 316221828Sgrehan 317221828Sgrehan sc = vmmdev_lookup2(cdev); 318241489Sneel if (sc == NULL) 319221828Sgrehan return (ENXIO); 320221828Sgrehan 321268891Sjhb error = 0; 322241489Sneel vcpu = -1; 323241489Sneel state_changed = 0; 324241489Sneel 325221828Sgrehan /* 326221828Sgrehan * Some VMM ioctls can operate only on vcpus that are not running. 327221828Sgrehan */ 328221828Sgrehan switch (cmd) { 329221828Sgrehan case VM_RUN: 330221828Sgrehan case VM_GET_REGISTER: 331221828Sgrehan case VM_SET_REGISTER: 332221828Sgrehan case VM_GET_SEGMENT_DESCRIPTOR: 333221828Sgrehan case VM_SET_SEGMENT_DESCRIPTOR: 334267427Sjhb case VM_INJECT_EXCEPTION: 335221828Sgrehan case VM_GET_CAPABILITY: 336221828Sgrehan case VM_SET_CAPABILITY: 337221828Sgrehan case VM_PPTDEV_MSI: 338241489Sneel case VM_PPTDEV_MSIX: 339240922Sneel case VM_SET_X2APIC_STATE: 340268976Sjhb case VM_GLA2GPA: 341270070Sgrehan case VM_ACTIVATE_CPU: 342270159Sgrehan case VM_SET_INTINFO: 343270159Sgrehan case VM_GET_INTINFO: 344284894Sneel case VM_RESTART_INSTRUCTION: 345221828Sgrehan /* 346221828Sgrehan * XXX fragile, handle with care 347221828Sgrehan * Assumes that the first field of the ioctl data is the vcpu. 348221828Sgrehan */ 349221828Sgrehan vcpu = *(int *)data; 350295124Sgrehan error = vcpu_lock_one(sc, vcpu); 351241489Sneel if (error) 352221828Sgrehan goto done; 353241489Sneel state_changed = 1; 354241489Sneel break; 355241489Sneel 356241489Sneel case VM_MAP_PPTDEV_MMIO: 357241489Sneel case VM_BIND_PPTDEV: 358241489Sneel case VM_UNBIND_PPTDEV: 359295124Sgrehan case VM_ALLOC_MEMSEG: 360295124Sgrehan case VM_MMAP_MEMSEG: 361270071Sgrehan case VM_REINIT: 362241489Sneel /* 363241489Sneel * ioctls that operate on the entire virtual machine must 364241489Sneel * prevent all vcpus from running. 365241489Sneel */ 366295124Sgrehan error = vcpu_lock_all(sc); 367295124Sgrehan if (error) 368241489Sneel goto done; 369241489Sneel state_changed = 2; 370221828Sgrehan break; 371241489Sneel 372295124Sgrehan case VM_GET_MEMSEG: 373295124Sgrehan case VM_MMAP_GETNEXT: 374295124Sgrehan /* 375295124Sgrehan * Lock a vcpu to make sure that the memory map cannot be 376295124Sgrehan * modified while it is being inspected. 377295124Sgrehan */ 378295124Sgrehan vcpu = VM_MAXCPU - 1; 379295124Sgrehan error = vcpu_lock_one(sc, vcpu); 380295124Sgrehan if (error) 381295124Sgrehan goto done; 382295124Sgrehan state_changed = 1; 383295124Sgrehan break; 384295124Sgrehan 385221828Sgrehan default: 386221828Sgrehan break; 387221828Sgrehan } 388221828Sgrehan 389221828Sgrehan switch(cmd) { 390221828Sgrehan case VM_RUN: 391221828Sgrehan vmrun = (struct vm_run *)data; 392221828Sgrehan error = vm_run(sc->vm, vmrun); 393221828Sgrehan break; 394268935Sjhb case VM_SUSPEND: 395268935Sjhb vmsuspend = (struct vm_suspend *)data; 396268935Sjhb error = vm_suspend(sc->vm, vmsuspend->how); 397268935Sjhb break; 398270071Sgrehan case VM_REINIT: 399270071Sgrehan error = vm_reinit(sc->vm); 400270071Sgrehan break; 401221828Sgrehan case VM_STAT_DESC: { 402221828Sgrehan statdesc = (struct vm_stat_desc *)data; 403250427Sneel error = vmm_stat_desc_copy(statdesc->index, 404250427Sneel statdesc->desc, sizeof(statdesc->desc)); 405221828Sgrehan break; 406221828Sgrehan } 407221828Sgrehan case VM_STATS: { 408250427Sneel CTASSERT(MAX_VM_STATS >= MAX_VMM_STAT_ELEMS); 409221828Sgrehan vmstats = (struct vm_stats *)data; 410221828Sgrehan getmicrotime(&vmstats->tv); 411221828Sgrehan error = vmm_stat_copy(sc->vm, vmstats->cpuid, 412221828Sgrehan &vmstats->num_entries, vmstats->statbuf); 413221828Sgrehan break; 414221828Sgrehan } 415221828Sgrehan case VM_PPTDEV_MSI: 416221828Sgrehan pptmsi = (struct vm_pptdev_msi *)data; 417221828Sgrehan error = ppt_setup_msi(sc->vm, pptmsi->vcpu, 418221828Sgrehan pptmsi->bus, pptmsi->slot, pptmsi->func, 419262350Sjhb pptmsi->addr, pptmsi->msg, 420221828Sgrehan pptmsi->numvec); 421221828Sgrehan break; 422234761Sgrehan case VM_PPTDEV_MSIX: 423234761Sgrehan pptmsix = (struct vm_pptdev_msix *)data; 424234761Sgrehan error = ppt_setup_msix(sc->vm, pptmsix->vcpu, 425234761Sgrehan pptmsix->bus, pptmsix->slot, 426234761Sgrehan pptmsix->func, pptmsix->idx, 427262350Sjhb pptmsix->addr, pptmsix->msg, 428262350Sjhb pptmsix->vector_control); 429234761Sgrehan break; 430221828Sgrehan case VM_MAP_PPTDEV_MMIO: 431221828Sgrehan pptmmio = (struct vm_pptdev_mmio *)data; 432221828Sgrehan error = ppt_map_mmio(sc->vm, pptmmio->bus, pptmmio->slot, 433221828Sgrehan pptmmio->func, pptmmio->gpa, pptmmio->len, 434221828Sgrehan pptmmio->hpa); 435221828Sgrehan break; 436221828Sgrehan case VM_BIND_PPTDEV: 437221828Sgrehan pptdev = (struct vm_pptdev *)data; 438256072Sneel error = vm_assign_pptdev(sc->vm, pptdev->bus, pptdev->slot, 439256072Sneel pptdev->func); 440221828Sgrehan break; 441221828Sgrehan case VM_UNBIND_PPTDEV: 442221828Sgrehan pptdev = (struct vm_pptdev *)data; 443256072Sneel error = vm_unassign_pptdev(sc->vm, pptdev->bus, pptdev->slot, 444256072Sneel pptdev->func); 445221828Sgrehan break; 446267427Sjhb case VM_INJECT_EXCEPTION: 447267427Sjhb vmexc = (struct vm_exception *)data; 448284894Sneel error = vm_inject_exception(sc->vm, vmexc->cpuid, 449284894Sneel vmexc->vector, vmexc->error_code_valid, vmexc->error_code, 450284894Sneel vmexc->restart_instruction); 451221828Sgrehan break; 452221828Sgrehan case VM_INJECT_NMI: 453221828Sgrehan vmnmi = (struct vm_nmi *)data; 454221828Sgrehan error = vm_inject_nmi(sc->vm, vmnmi->cpuid); 455221828Sgrehan break; 456221828Sgrehan case VM_LAPIC_IRQ: 457221828Sgrehan vmirq = (struct vm_lapic_irq *)data; 458261088Sjhb error = lapic_intr_edge(sc->vm, vmirq->cpuid, vmirq->vector); 459221828Sgrehan break; 460262350Sjhb case VM_LAPIC_LOCAL_IRQ: 461262350Sjhb vmirq = (struct vm_lapic_irq *)data; 462262350Sjhb error = lapic_set_local_intr(sc->vm, vmirq->cpuid, 463262350Sjhb vmirq->vector); 464262350Sjhb break; 465262350Sjhb case VM_LAPIC_MSI: 466262350Sjhb vmmsi = (struct vm_lapic_msi *)data; 467262350Sjhb error = lapic_intr_msi(sc->vm, vmmsi->addr, vmmsi->msg); 468262350Sjhb break; 469261088Sjhb case VM_IOAPIC_ASSERT_IRQ: 470261088Sjhb ioapic_irq = (struct vm_ioapic_irq *)data; 471261088Sjhb error = vioapic_assert_irq(sc->vm, ioapic_irq->irq); 472261088Sjhb break; 473261088Sjhb case VM_IOAPIC_DEASSERT_IRQ: 474261088Sjhb ioapic_irq = (struct vm_ioapic_irq *)data; 475261088Sjhb error = vioapic_deassert_irq(sc->vm, ioapic_irq->irq); 476261088Sjhb break; 477261088Sjhb case VM_IOAPIC_PULSE_IRQ: 478261088Sjhb ioapic_irq = (struct vm_ioapic_irq *)data; 479261088Sjhb error = vioapic_pulse_irq(sc->vm, ioapic_irq->irq); 480261088Sjhb break; 481267393Sjhb case VM_IOAPIC_PINCOUNT: 482267393Sjhb *(int *)data = vioapic_pincount(sc->vm); 483267393Sjhb break; 484268891Sjhb case VM_ISA_ASSERT_IRQ: 485268891Sjhb isa_irq = (struct vm_isa_irq *)data; 486268891Sjhb error = vatpic_assert_irq(sc->vm, isa_irq->atpic_irq); 487268891Sjhb if (error == 0 && isa_irq->ioapic_irq != -1) 488268891Sjhb error = vioapic_assert_irq(sc->vm, 489268891Sjhb isa_irq->ioapic_irq); 490268891Sjhb break; 491268891Sjhb case VM_ISA_DEASSERT_IRQ: 492268891Sjhb isa_irq = (struct vm_isa_irq *)data; 493268891Sjhb error = vatpic_deassert_irq(sc->vm, isa_irq->atpic_irq); 494268891Sjhb if (error == 0 && isa_irq->ioapic_irq != -1) 495268891Sjhb error = vioapic_deassert_irq(sc->vm, 496268891Sjhb isa_irq->ioapic_irq); 497268891Sjhb break; 498268891Sjhb case VM_ISA_PULSE_IRQ: 499268891Sjhb isa_irq = (struct vm_isa_irq *)data; 500268891Sjhb error = vatpic_pulse_irq(sc->vm, isa_irq->atpic_irq); 501268891Sjhb if (error == 0 && isa_irq->ioapic_irq != -1) 502268891Sjhb error = vioapic_pulse_irq(sc->vm, isa_irq->ioapic_irq); 503268891Sjhb break; 504268972Sjhb case VM_ISA_SET_IRQ_TRIGGER: 505268972Sjhb isa_irq_trigger = (struct vm_isa_irq_trigger *)data; 506268972Sjhb error = vatpic_set_irq_trigger(sc->vm, 507268972Sjhb isa_irq_trigger->atpic_irq, isa_irq_trigger->trigger); 508268972Sjhb break; 509295124Sgrehan case VM_MMAP_GETNEXT: 510295124Sgrehan mm = (struct vm_memmap *)data; 511295124Sgrehan error = vm_mmap_getnext(sc->vm, &mm->gpa, &mm->segid, 512295124Sgrehan &mm->segoff, &mm->len, &mm->prot, &mm->flags); 513221828Sgrehan break; 514295124Sgrehan case VM_MMAP_MEMSEG: 515295124Sgrehan mm = (struct vm_memmap *)data; 516295124Sgrehan error = vm_mmap_memseg(sc->vm, mm->gpa, mm->segid, mm->segoff, 517295124Sgrehan mm->len, mm->prot, mm->flags); 518221828Sgrehan break; 519295124Sgrehan case VM_ALLOC_MEMSEG: 520295124Sgrehan error = alloc_memseg(sc, (struct vm_memseg *)data); 521295124Sgrehan break; 522295124Sgrehan case VM_GET_MEMSEG: 523295124Sgrehan error = get_memseg(sc, (struct vm_memseg *)data); 524295124Sgrehan break; 525221828Sgrehan case VM_GET_REGISTER: 526221828Sgrehan vmreg = (struct vm_register *)data; 527221828Sgrehan error = vm_get_register(sc->vm, vmreg->cpuid, vmreg->regnum, 528221828Sgrehan &vmreg->regval); 529221828Sgrehan break; 530221828Sgrehan case VM_SET_REGISTER: 531221828Sgrehan vmreg = (struct vm_register *)data; 532221828Sgrehan error = vm_set_register(sc->vm, vmreg->cpuid, vmreg->regnum, 533221828Sgrehan vmreg->regval); 534221828Sgrehan break; 535221828Sgrehan case VM_SET_SEGMENT_DESCRIPTOR: 536221828Sgrehan vmsegdesc = (struct vm_seg_desc *)data; 537221828Sgrehan error = vm_set_seg_desc(sc->vm, vmsegdesc->cpuid, 538221828Sgrehan vmsegdesc->regnum, 539221828Sgrehan &vmsegdesc->desc); 540221828Sgrehan break; 541221828Sgrehan case VM_GET_SEGMENT_DESCRIPTOR: 542221828Sgrehan vmsegdesc = (struct vm_seg_desc *)data; 543221828Sgrehan error = vm_get_seg_desc(sc->vm, vmsegdesc->cpuid, 544221828Sgrehan vmsegdesc->regnum, 545221828Sgrehan &vmsegdesc->desc); 546221828Sgrehan break; 547221828Sgrehan case VM_GET_CAPABILITY: 548221828Sgrehan vmcap = (struct vm_capability *)data; 549221828Sgrehan error = vm_get_capability(sc->vm, vmcap->cpuid, 550221828Sgrehan vmcap->captype, 551221828Sgrehan &vmcap->capval); 552221828Sgrehan break; 553221828Sgrehan case VM_SET_CAPABILITY: 554221828Sgrehan vmcap = (struct vm_capability *)data; 555221828Sgrehan error = vm_set_capability(sc->vm, vmcap->cpuid, 556221828Sgrehan vmcap->captype, 557221828Sgrehan vmcap->capval); 558221828Sgrehan break; 559240922Sneel case VM_SET_X2APIC_STATE: 560240922Sneel x2apic = (struct vm_x2apic *)data; 561240922Sneel error = vm_set_x2apic_state(sc->vm, 562240922Sneel x2apic->cpuid, x2apic->state); 563240922Sneel break; 564240922Sneel case VM_GET_X2APIC_STATE: 565240922Sneel x2apic = (struct vm_x2apic *)data; 566240922Sneel error = vm_get_x2apic_state(sc->vm, 567240922Sneel x2apic->cpuid, &x2apic->state); 568240922Sneel break; 569256072Sneel case VM_GET_GPA_PMAP: 570256072Sneel gpapte = (struct vm_gpa_pte *)data; 571256072Sneel pmap_get_mapping(vmspace_pmap(vm_get_vmspace(sc->vm)), 572256072Sneel gpapte->gpa, gpapte->pte, &gpapte->ptenum); 573256072Sneel error = 0; 574256072Sneel break; 575261088Sjhb case VM_GET_HPET_CAPABILITIES: 576261088Sjhb error = vhpet_getcap((struct vm_hpet_cap *)data); 577261088Sjhb break; 578268976Sjhb case VM_GLA2GPA: { 579268976Sjhb CTASSERT(PROT_READ == VM_PROT_READ); 580268976Sjhb CTASSERT(PROT_WRITE == VM_PROT_WRITE); 581268976Sjhb CTASSERT(PROT_EXEC == VM_PROT_EXECUTE); 582268976Sjhb gg = (struct vm_gla2gpa *)data; 583284899Sneel error = vm_gla2gpa(sc->vm, gg->vcpuid, &gg->paging, gg->gla, 584284900Sneel gg->prot, &gg->gpa, &gg->fault); 585284900Sneel KASSERT(error == 0 || error == EFAULT, 586284899Sneel ("%s: vm_gla2gpa unknown error %d", __func__, error)); 587268976Sjhb break; 588268976Sjhb } 589270070Sgrehan case VM_ACTIVATE_CPU: 590270070Sgrehan vac = (struct vm_activate_cpu *)data; 591270070Sgrehan error = vm_activate_cpu(sc->vm, vac->vcpuid); 592270070Sgrehan break; 593270070Sgrehan case VM_GET_CPUS: 594270070Sgrehan error = 0; 595270070Sgrehan vm_cpuset = (struct vm_cpuset *)data; 596270070Sgrehan size = vm_cpuset->cpusetsize; 597270070Sgrehan if (size < sizeof(cpuset_t) || size > CPU_MAXSIZE / NBBY) { 598270070Sgrehan error = ERANGE; 599270070Sgrehan break; 600270070Sgrehan } 601270070Sgrehan cpuset = malloc(size, M_TEMP, M_WAITOK | M_ZERO); 602270070Sgrehan if (vm_cpuset->which == VM_ACTIVE_CPUS) 603270070Sgrehan *cpuset = vm_active_cpus(sc->vm); 604270070Sgrehan else if (vm_cpuset->which == VM_SUSPENDED_CPUS) 605270070Sgrehan *cpuset = vm_suspended_cpus(sc->vm); 606270070Sgrehan else 607270070Sgrehan error = EINVAL; 608270070Sgrehan if (error == 0) 609270070Sgrehan error = copyout(cpuset, vm_cpuset->cpus, size); 610270070Sgrehan free(cpuset, M_TEMP); 611270070Sgrehan break; 612270159Sgrehan case VM_SET_INTINFO: 613270159Sgrehan vmii = (struct vm_intinfo *)data; 614270159Sgrehan error = vm_exit_intinfo(sc->vm, vmii->vcpuid, vmii->info1); 615270159Sgrehan break; 616270159Sgrehan case VM_GET_INTINFO: 617270159Sgrehan vmii = (struct vm_intinfo *)data; 618270159Sgrehan error = vm_get_intinfo(sc->vm, vmii->vcpuid, &vmii->info1, 619270159Sgrehan &vmii->info2); 620270159Sgrehan break; 621284894Sneel case VM_RTC_WRITE: 622284894Sneel rtcdata = (struct vm_rtc_data *)data; 623284894Sneel error = vrtc_nvram_write(sc->vm, rtcdata->offset, 624284894Sneel rtcdata->value); 625284894Sneel break; 626284894Sneel case VM_RTC_READ: 627284894Sneel rtcdata = (struct vm_rtc_data *)data; 628284894Sneel error = vrtc_nvram_read(sc->vm, rtcdata->offset, 629284894Sneel &rtcdata->value); 630284894Sneel break; 631284894Sneel case VM_RTC_SETTIME: 632284894Sneel rtctime = (struct vm_rtc_time *)data; 633284894Sneel error = vrtc_set_time(sc->vm, rtctime->secs); 634284894Sneel break; 635284894Sneel case VM_RTC_GETTIME: 636284894Sneel error = 0; 637284894Sneel rtctime = (struct vm_rtc_time *)data; 638284894Sneel rtctime->secs = vrtc_get_time(sc->vm); 639284894Sneel break; 640284894Sneel case VM_RESTART_INSTRUCTION: 641284894Sneel error = vm_restart_instruction(sc->vm, vcpu); 642284894Sneel break; 643221828Sgrehan default: 644221828Sgrehan error = ENOTTY; 645221828Sgrehan break; 646221828Sgrehan } 647241489Sneel 648295124Sgrehan if (state_changed == 1) 649295124Sgrehan vcpu_unlock_one(sc, vcpu); 650295124Sgrehan else if (state_changed == 2) 651295124Sgrehan vcpu_unlock_all(sc); 652241489Sneel 653221828Sgrehandone: 654256072Sneel /* Make sure that no handler returns a bogus value like ERESTART */ 655256072Sneel KASSERT(error >= 0, ("vmmdev_ioctl: invalid error return %d", error)); 656221828Sgrehan return (error); 657221828Sgrehan} 658221828Sgrehan 659221828Sgrehanstatic int 660295124Sgrehanvmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t mapsize, 661295124Sgrehan struct vm_object **objp, int nprot) 662221828Sgrehan{ 663221828Sgrehan struct vmmdev_softc *sc; 664295124Sgrehan vm_paddr_t gpa; 665295124Sgrehan size_t len; 666295124Sgrehan vm_ooffset_t segoff, first, last; 667295124Sgrehan int error, found, segid; 668295124Sgrehan bool sysmem; 669221828Sgrehan 670295124Sgrehan first = *offset; 671295124Sgrehan last = first + mapsize; 672295124Sgrehan if ((nprot & PROT_EXEC) || first < 0 || first >= last) 673295124Sgrehan return (EINVAL); 674295124Sgrehan 675221828Sgrehan sc = vmmdev_lookup2(cdev); 676295124Sgrehan if (sc == NULL) { 677295124Sgrehan /* virtual machine is in the process of being created */ 678295124Sgrehan return (EINVAL); 679295124Sgrehan } 680221828Sgrehan 681295124Sgrehan /* 682295124Sgrehan * Get a read lock on the guest memory map by freezing any vcpu. 683295124Sgrehan */ 684295124Sgrehan error = vcpu_lock_one(sc, VM_MAXCPU - 1); 685295124Sgrehan if (error) 686295124Sgrehan return (error); 687295124Sgrehan 688295124Sgrehan gpa = 0; 689295124Sgrehan found = 0; 690295124Sgrehan while (!found) { 691295124Sgrehan error = vm_mmap_getnext(sc->vm, &gpa, &segid, &segoff, &len, 692295124Sgrehan NULL, NULL); 693295124Sgrehan if (error) 694295124Sgrehan break; 695295124Sgrehan 696295124Sgrehan if (first >= gpa && last <= gpa + len) 697295124Sgrehan found = 1; 698295124Sgrehan else 699295124Sgrehan gpa += len; 700295124Sgrehan } 701295124Sgrehan 702295124Sgrehan if (found) { 703295124Sgrehan error = vm_get_memseg(sc->vm, segid, &len, &sysmem, objp); 704295124Sgrehan KASSERT(error == 0 && *objp != NULL, 705295124Sgrehan ("%s: invalid memory segment %d", __func__, segid)); 706295124Sgrehan if (sysmem) { 707295124Sgrehan vm_object_reference(*objp); 708295124Sgrehan *offset = segoff + (first - gpa); 709295124Sgrehan } else { 710295124Sgrehan error = EINVAL; 711295124Sgrehan } 712295124Sgrehan } 713295124Sgrehan vcpu_unlock_one(sc, VM_MAXCPU - 1); 714221828Sgrehan return (error); 715221828Sgrehan} 716221828Sgrehan 717221828Sgrehanstatic void 718256651Sneelvmmdev_destroy(void *arg) 719221828Sgrehan{ 720256651Sneel struct vmmdev_softc *sc = arg; 721295124Sgrehan struct devmem_softc *dsc; 722295124Sgrehan int error; 723256651Sneel 724295124Sgrehan error = vcpu_lock_all(sc); 725295124Sgrehan KASSERT(error == 0, ("%s: error %d freezing vcpus", __func__, error)); 726295124Sgrehan 727295124Sgrehan while ((dsc = SLIST_FIRST(&sc->devmem)) != NULL) { 728295124Sgrehan KASSERT(dsc->cdev == NULL, ("%s: devmem not free", __func__)); 729295124Sgrehan SLIST_REMOVE_HEAD(&sc->devmem, link); 730295124Sgrehan free(dsc->name, M_VMMDEV); 731295124Sgrehan free(dsc, M_VMMDEV); 732295124Sgrehan } 733295124Sgrehan 734256651Sneel if (sc->cdev != NULL) 735241454Sneel destroy_dev(sc->cdev); 736241454Sneel 737256651Sneel if (sc->vm != NULL) 738241454Sneel vm_destroy(sc->vm); 739241454Sneel 740256651Sneel if ((sc->flags & VSC_LINKED) != 0) { 741241454Sneel mtx_lock(&vmmdev_mtx); 742241454Sneel SLIST_REMOVE(&head, sc, vmmdev_softc, link); 743241454Sneel mtx_unlock(&vmmdev_mtx); 744241454Sneel } 745241454Sneel 746221828Sgrehan free(sc, M_VMMDEV); 747221828Sgrehan} 748221828Sgrehan 749221828Sgrehanstatic int 750221828Sgrehansysctl_vmm_destroy(SYSCTL_HANDLER_ARGS) 751221828Sgrehan{ 752221828Sgrehan int error; 753221828Sgrehan char buf[VM_MAX_NAMELEN]; 754295124Sgrehan struct devmem_softc *dsc; 755221828Sgrehan struct vmmdev_softc *sc; 756256651Sneel struct cdev *cdev; 757221828Sgrehan 758221828Sgrehan strlcpy(buf, "beavis", sizeof(buf)); 759221828Sgrehan error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 760221828Sgrehan if (error != 0 || req->newptr == NULL) 761221828Sgrehan return (error); 762221828Sgrehan 763221828Sgrehan mtx_lock(&vmmdev_mtx); 764221828Sgrehan sc = vmmdev_lookup(buf); 765256651Sneel if (sc == NULL || sc->cdev == NULL) { 766221828Sgrehan mtx_unlock(&vmmdev_mtx); 767221828Sgrehan return (EINVAL); 768221828Sgrehan } 769241454Sneel 770256651Sneel /* 771256651Sneel * The 'cdev' will be destroyed asynchronously when 'si_threadcount' 772256651Sneel * goes down to 0 so we should not do it again in the callback. 773295124Sgrehan * 774295124Sgrehan * Setting 'sc->cdev' to NULL is also used to indicate that the VM 775295124Sgrehan * is scheduled for destruction. 776256651Sneel */ 777256651Sneel cdev = sc->cdev; 778256651Sneel sc->cdev = NULL; 779221828Sgrehan mtx_unlock(&vmmdev_mtx); 780241454Sneel 781256651Sneel /* 782295124Sgrehan * Schedule all cdevs to be destroyed: 783256651Sneel * 784295124Sgrehan * - any new operations on the 'cdev' will return an error (ENXIO). 785256651Sneel * 786256651Sneel * - when the 'si_threadcount' dwindles down to zero the 'cdev' will 787256651Sneel * be destroyed and the callback will be invoked in a taskqueue 788256651Sneel * context. 789295124Sgrehan * 790295124Sgrehan * - the 'devmem' cdevs are destroyed before the virtual machine 'cdev' 791256651Sneel */ 792295124Sgrehan SLIST_FOREACH(dsc, &sc->devmem, link) { 793295124Sgrehan KASSERT(dsc->cdev != NULL, ("devmem cdev already destroyed")); 794295124Sgrehan destroy_dev_sched_cb(dsc->cdev, devmem_destroy, dsc); 795295124Sgrehan } 796256651Sneel destroy_dev_sched_cb(cdev, vmmdev_destroy, sc); 797221828Sgrehan return (0); 798221828Sgrehan} 799221828SgrehanSYSCTL_PROC(_hw_vmm, OID_AUTO, destroy, CTLTYPE_STRING | CTLFLAG_RW, 800221828Sgrehan NULL, 0, sysctl_vmm_destroy, "A", NULL); 801221828Sgrehan 802221828Sgrehanstatic struct cdevsw vmmdevsw = { 803221828Sgrehan .d_name = "vmmdev", 804221828Sgrehan .d_version = D_VERSION, 805221828Sgrehan .d_ioctl = vmmdev_ioctl, 806256072Sneel .d_mmap_single = vmmdev_mmap_single, 807221828Sgrehan .d_read = vmmdev_rw, 808221828Sgrehan .d_write = vmmdev_rw, 809221828Sgrehan}; 810221828Sgrehan 811221828Sgrehanstatic int 812221828Sgrehansysctl_vmm_create(SYSCTL_HANDLER_ARGS) 813221828Sgrehan{ 814221828Sgrehan int error; 815221828Sgrehan struct vm *vm; 816256651Sneel struct cdev *cdev; 817241454Sneel struct vmmdev_softc *sc, *sc2; 818221828Sgrehan char buf[VM_MAX_NAMELEN]; 819221828Sgrehan 820221828Sgrehan strlcpy(buf, "beavis", sizeof(buf)); 821221828Sgrehan error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 822221828Sgrehan if (error != 0 || req->newptr == NULL) 823221828Sgrehan return (error); 824221828Sgrehan 825221828Sgrehan mtx_lock(&vmmdev_mtx); 826221828Sgrehan sc = vmmdev_lookup(buf); 827241454Sneel mtx_unlock(&vmmdev_mtx); 828241454Sneel if (sc != NULL) 829221828Sgrehan return (EEXIST); 830221828Sgrehan 831249396Sneel error = vm_create(buf, &vm); 832249396Sneel if (error != 0) 833249396Sneel return (error); 834221828Sgrehan 835221828Sgrehan sc = malloc(sizeof(struct vmmdev_softc), M_VMMDEV, M_WAITOK | M_ZERO); 836221828Sgrehan sc->vm = vm; 837295124Sgrehan SLIST_INIT(&sc->devmem); 838241454Sneel 839241454Sneel /* 840241454Sneel * Lookup the name again just in case somebody sneaked in when we 841241454Sneel * dropped the lock. 842241454Sneel */ 843241454Sneel mtx_lock(&vmmdev_mtx); 844241454Sneel sc2 = vmmdev_lookup(buf); 845256651Sneel if (sc2 == NULL) { 846241454Sneel SLIST_INSERT_HEAD(&head, sc, link); 847256651Sneel sc->flags |= VSC_LINKED; 848256651Sneel } 849241454Sneel mtx_unlock(&vmmdev_mtx); 850241454Sneel 851241454Sneel if (sc2 != NULL) { 852256651Sneel vmmdev_destroy(sc); 853241454Sneel return (EEXIST); 854241454Sneel } 855241454Sneel 856256651Sneel error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &vmmdevsw, NULL, 857249435Sneel UID_ROOT, GID_WHEEL, 0600, "vmm/%s", buf); 858249435Sneel if (error != 0) { 859256651Sneel vmmdev_destroy(sc); 860249435Sneel return (error); 861249435Sneel } 862256651Sneel 863256651Sneel mtx_lock(&vmmdev_mtx); 864256651Sneel sc->cdev = cdev; 865221828Sgrehan sc->cdev->si_drv1 = sc; 866256651Sneel mtx_unlock(&vmmdev_mtx); 867221828Sgrehan 868221828Sgrehan return (0); 869221828Sgrehan} 870221828SgrehanSYSCTL_PROC(_hw_vmm, OID_AUTO, create, CTLTYPE_STRING | CTLFLAG_RW, 871221828Sgrehan NULL, 0, sysctl_vmm_create, "A", NULL); 872221828Sgrehan 873221828Sgrehanvoid 874221828Sgrehanvmmdev_init(void) 875221828Sgrehan{ 876221828Sgrehan mtx_init(&vmmdev_mtx, "vmm device mutex", NULL, MTX_DEF); 877221828Sgrehan} 878221828Sgrehan 879241454Sneelint 880221828Sgrehanvmmdev_cleanup(void) 881221828Sgrehan{ 882241454Sneel int error; 883221828Sgrehan 884241454Sneel if (SLIST_EMPTY(&head)) 885241454Sneel error = 0; 886241454Sneel else 887241454Sneel error = EBUSY; 888221828Sgrehan 889241454Sneel return (error); 890221828Sgrehan} 891295124Sgrehan 892295124Sgrehanstatic int 893295124Sgrehandevmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t len, 894295124Sgrehan struct vm_object **objp, int nprot) 895295124Sgrehan{ 896295124Sgrehan struct devmem_softc *dsc; 897295124Sgrehan vm_ooffset_t first, last; 898295124Sgrehan size_t seglen; 899295124Sgrehan int error; 900295124Sgrehan bool sysmem; 901295124Sgrehan 902295124Sgrehan dsc = cdev->si_drv1; 903295124Sgrehan if (dsc == NULL) { 904295124Sgrehan /* 'cdev' has been created but is not ready for use */ 905295124Sgrehan return (ENXIO); 906295124Sgrehan } 907295124Sgrehan 908295124Sgrehan first = *offset; 909295124Sgrehan last = *offset + len; 910295124Sgrehan if ((nprot & PROT_EXEC) || first < 0 || first >= last) 911295124Sgrehan return (EINVAL); 912295124Sgrehan 913295124Sgrehan error = vcpu_lock_one(dsc->sc, VM_MAXCPU - 1); 914295124Sgrehan if (error) 915295124Sgrehan return (error); 916295124Sgrehan 917295124Sgrehan error = vm_get_memseg(dsc->sc->vm, dsc->segid, &seglen, &sysmem, objp); 918295124Sgrehan KASSERT(error == 0 && !sysmem && *objp != NULL, 919295124Sgrehan ("%s: invalid devmem segment %d", __func__, dsc->segid)); 920295124Sgrehan 921295124Sgrehan vcpu_unlock_one(dsc->sc, VM_MAXCPU - 1); 922295124Sgrehan 923295124Sgrehan if (seglen >= last) { 924295124Sgrehan vm_object_reference(*objp); 925295124Sgrehan return (0); 926295124Sgrehan } else { 927295124Sgrehan return (EINVAL); 928295124Sgrehan } 929295124Sgrehan} 930295124Sgrehan 931295124Sgrehanstatic struct cdevsw devmemsw = { 932295124Sgrehan .d_name = "devmem", 933295124Sgrehan .d_version = D_VERSION, 934295124Sgrehan .d_mmap_single = devmem_mmap_single, 935295124Sgrehan}; 936295124Sgrehan 937295124Sgrehanstatic int 938295124Sgrehandevmem_create_cdev(const char *vmname, int segid, char *devname) 939295124Sgrehan{ 940295124Sgrehan struct devmem_softc *dsc; 941295124Sgrehan struct vmmdev_softc *sc; 942295124Sgrehan struct cdev *cdev; 943295124Sgrehan int error; 944295124Sgrehan 945295124Sgrehan error = make_dev_p(MAKEDEV_CHECKNAME, &cdev, &devmemsw, NULL, 946295124Sgrehan UID_ROOT, GID_WHEEL, 0600, "vmm.io/%s.%s", vmname, devname); 947295124Sgrehan if (error) 948295124Sgrehan return (error); 949295124Sgrehan 950295124Sgrehan dsc = malloc(sizeof(struct devmem_softc), M_VMMDEV, M_WAITOK | M_ZERO); 951295124Sgrehan 952295124Sgrehan mtx_lock(&vmmdev_mtx); 953295124Sgrehan sc = vmmdev_lookup(vmname); 954295124Sgrehan KASSERT(sc != NULL, ("%s: vm %s softc not found", __func__, vmname)); 955295124Sgrehan if (sc->cdev == NULL) { 956295124Sgrehan /* virtual machine is being created or destroyed */ 957295124Sgrehan mtx_unlock(&vmmdev_mtx); 958295124Sgrehan free(dsc, M_VMMDEV); 959295124Sgrehan destroy_dev_sched_cb(cdev, NULL, 0); 960295124Sgrehan return (ENODEV); 961295124Sgrehan } 962295124Sgrehan 963295124Sgrehan dsc->segid = segid; 964295124Sgrehan dsc->name = devname; 965295124Sgrehan dsc->cdev = cdev; 966295124Sgrehan dsc->sc = sc; 967295124Sgrehan SLIST_INSERT_HEAD(&sc->devmem, dsc, link); 968295124Sgrehan mtx_unlock(&vmmdev_mtx); 969295124Sgrehan 970295124Sgrehan /* The 'cdev' is ready for use after 'si_drv1' is initialized */ 971295124Sgrehan cdev->si_drv1 = dsc; 972295124Sgrehan return (0); 973295124Sgrehan} 974295124Sgrehan 975295124Sgrehanstatic void 976295124Sgrehandevmem_destroy(void *arg) 977295124Sgrehan{ 978295124Sgrehan struct devmem_softc *dsc = arg; 979295124Sgrehan 980295124Sgrehan KASSERT(dsc->cdev, ("%s: devmem cdev already destroyed", __func__)); 981295124Sgrehan dsc->cdev = NULL; 982295124Sgrehan dsc->sc = NULL; 983295124Sgrehan} 984