Changeset View
Changeset View
Standalone View
Standalone View
head/sys/amd64/vmm/vmm_dev.c
Show First 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | vmm_priv_check(struct ucred *ucred) | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
vcpu_lock_one(struct vmmdev_softc *sc, int vcpu) | vcpu_lock_one(struct vmmdev_softc *sc, int vcpu) | ||||
{ | { | ||||
int error; | int error; | ||||
if (vcpu < 0 || vcpu >= VM_MAXCPU) | if (vcpu < 0 || vcpu >= vm_get_maxcpus(sc->vm)) | ||||
return (EINVAL); | return (EINVAL); | ||||
error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); | error = vcpu_set_state(sc->vm, vcpu, VCPU_FROZEN, true); | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
vcpu_unlock_one(struct vmmdev_softc *sc, int vcpu) | vcpu_unlock_one(struct vmmdev_softc *sc, int vcpu) | ||||
{ | { | ||||
enum vcpu_state state; | enum vcpu_state state; | ||||
state = vcpu_get_state(sc->vm, vcpu, NULL); | state = vcpu_get_state(sc->vm, vcpu, NULL); | ||||
if (state != VCPU_FROZEN) { | if (state != VCPU_FROZEN) { | ||||
panic("vcpu %s(%d) has invalid state %d", vm_name(sc->vm), | panic("vcpu %s(%d) has invalid state %d", vm_name(sc->vm), | ||||
vcpu, state); | vcpu, state); | ||||
} | } | ||||
vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); | vcpu_set_state(sc->vm, vcpu, VCPU_IDLE, false); | ||||
} | } | ||||
static int | static int | ||||
vcpu_lock_all(struct vmmdev_softc *sc) | vcpu_lock_all(struct vmmdev_softc *sc) | ||||
{ | { | ||||
int error, vcpu; | int error, vcpu; | ||||
uint16_t maxcpus; | |||||
for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) { | maxcpus = vm_get_maxcpus(sc->vm); | ||||
for (vcpu = 0; vcpu < maxcpus; vcpu++) { | |||||
error = vcpu_lock_one(sc, vcpu); | error = vcpu_lock_one(sc, vcpu); | ||||
if (error) | if (error) | ||||
break; | break; | ||||
} | } | ||||
if (error) { | if (error) { | ||||
while (--vcpu >= 0) | while (--vcpu >= 0) | ||||
vcpu_unlock_one(sc, vcpu); | vcpu_unlock_one(sc, vcpu); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
vcpu_unlock_all(struct vmmdev_softc *sc) | vcpu_unlock_all(struct vmmdev_softc *sc) | ||||
{ | { | ||||
int vcpu; | int vcpu; | ||||
uint16_t maxcpus; | |||||
for (vcpu = 0; vcpu < VM_MAXCPU; vcpu++) | maxcpus = vm_get_maxcpus(sc->vm); | ||||
for (vcpu = 0; vcpu < maxcpus; vcpu++) | |||||
vcpu_unlock_one(sc, vcpu); | vcpu_unlock_one(sc, vcpu); | ||||
} | } | ||||
static struct vmmdev_softc * | static struct vmmdev_softc * | ||||
vmmdev_lookup(const char *name) | vmmdev_lookup(const char *name) | ||||
{ | { | ||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||
Show All 18 Lines | |||||
static int | static int | ||||
vmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) | vmmdev_rw(struct cdev *cdev, struct uio *uio, int flags) | ||||
{ | { | ||||
int error, off, c, prot; | int error, off, c, prot; | ||||
vm_paddr_t gpa, maxaddr; | vm_paddr_t gpa, maxaddr; | ||||
void *hpa, *cookie; | void *hpa, *cookie; | ||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||
uint16_t lastcpu; | |||||
error = vmm_priv_check(curthread->td_ucred); | error = vmm_priv_check(curthread->td_ucred); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
sc = vmmdev_lookup2(cdev); | sc = vmmdev_lookup2(cdev); | ||||
if (sc == NULL) | if (sc == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
/* | /* | ||||
* Get a read lock on the guest memory map by freezing any vcpu. | * Get a read lock on the guest memory map by freezing any vcpu. | ||||
*/ | */ | ||||
error = vcpu_lock_one(sc, VM_MAXCPU - 1); | lastcpu = vm_get_maxcpus(sc->vm) - 1; | ||||
error = vcpu_lock_one(sc, lastcpu); | |||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ); | prot = (uio->uio_rw == UIO_WRITE ? VM_PROT_WRITE : VM_PROT_READ); | ||||
maxaddr = vmm_sysmem_maxaddr(sc->vm); | maxaddr = vmm_sysmem_maxaddr(sc->vm); | ||||
while (uio->uio_resid > 0 && error == 0) { | while (uio->uio_resid > 0 && error == 0) { | ||||
gpa = uio->uio_offset; | gpa = uio->uio_offset; | ||||
off = gpa & PAGE_MASK; | off = gpa & PAGE_MASK; | ||||
c = min(uio->uio_resid, PAGE_SIZE - off); | c = min(uio->uio_resid, PAGE_SIZE - off); | ||||
/* | /* | ||||
* The VM has a hole in its physical memory map. If we want to | * The VM has a hole in its physical memory map. If we want to | ||||
* use 'dd' to inspect memory beyond the hole we need to | * use 'dd' to inspect memory beyond the hole we need to | ||||
* provide bogus data for memory that lies in the hole. | * provide bogus data for memory that lies in the hole. | ||||
* | * | ||||
* Since this device does not support lseek(2), dd(1) will | * Since this device does not support lseek(2), dd(1) will | ||||
* read(2) blocks of data to simulate the lseek(2). | * read(2) blocks of data to simulate the lseek(2). | ||||
*/ | */ | ||||
hpa = vm_gpa_hold(sc->vm, VM_MAXCPU - 1, gpa, c, prot, &cookie); | hpa = vm_gpa_hold(sc->vm, lastcpu, gpa, c, | ||||
prot, &cookie); | |||||
if (hpa == NULL) { | if (hpa == NULL) { | ||||
if (uio->uio_rw == UIO_READ && gpa < maxaddr) | if (uio->uio_rw == UIO_READ && gpa < maxaddr) | ||||
error = uiomove(__DECONST(void *, zero_region), | error = uiomove(__DECONST(void *, zero_region), | ||||
c, uio); | c, uio); | ||||
else | else | ||||
error = EFAULT; | error = EFAULT; | ||||
} else { | } else { | ||||
error = uiomove(hpa, c, uio); | error = uiomove(hpa, c, uio); | ||||
vm_gpa_release(cookie); | vm_gpa_release(cookie); | ||||
} | } | ||||
} | } | ||||
vcpu_unlock_one(sc, VM_MAXCPU - 1); | vcpu_unlock_one(sc, lastcpu); | ||||
return (error); | return (error); | ||||
} | } | ||||
CTASSERT(sizeof(((struct vm_memseg *)0)->name) >= SPECNAMELEN + 1); | CTASSERT(sizeof(((struct vm_memseg *)0)->name) >= SPECNAMELEN + 1); | ||||
static int | static int | ||||
get_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg) | get_memseg(struct vmmdev_softc *sc, struct vm_memseg *mseg) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 186 Lines • ▼ Show 20 Lines | case VM_REINIT: | ||||
break; | break; | ||||
case VM_GET_MEMSEG: | case VM_GET_MEMSEG: | ||||
case VM_MMAP_GETNEXT: | case VM_MMAP_GETNEXT: | ||||
/* | /* | ||||
* Lock a vcpu to make sure that the memory map cannot be | * Lock a vcpu to make sure that the memory map cannot be | ||||
* modified while it is being inspected. | * modified while it is being inspected. | ||||
*/ | */ | ||||
vcpu = VM_MAXCPU - 1; | vcpu = vm_get_maxcpus(sc->vm) - 1; | ||||
error = vcpu_lock_one(sc, vcpu); | error = vcpu_lock_one(sc, vcpu); | ||||
if (error) | if (error) | ||||
goto done; | goto done; | ||||
state_changed = 1; | state_changed = 1; | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 343 Lines • ▼ Show 20 Lines | |||||
vmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t mapsize, | vmmdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t mapsize, | ||||
struct vm_object **objp, int nprot) | struct vm_object **objp, int nprot) | ||||
{ | { | ||||
struct vmmdev_softc *sc; | struct vmmdev_softc *sc; | ||||
vm_paddr_t gpa; | vm_paddr_t gpa; | ||||
size_t len; | size_t len; | ||||
vm_ooffset_t segoff, first, last; | vm_ooffset_t segoff, first, last; | ||||
int error, found, segid; | int error, found, segid; | ||||
uint16_t lastcpu; | |||||
bool sysmem; | bool sysmem; | ||||
error = vmm_priv_check(curthread->td_ucred); | error = vmm_priv_check(curthread->td_ucred); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
first = *offset; | first = *offset; | ||||
last = first + mapsize; | last = first + mapsize; | ||||
if ((nprot & PROT_EXEC) || first < 0 || first >= last) | if ((nprot & PROT_EXEC) || first < 0 || first >= last) | ||||
return (EINVAL); | return (EINVAL); | ||||
sc = vmmdev_lookup2(cdev); | sc = vmmdev_lookup2(cdev); | ||||
if (sc == NULL) { | if (sc == NULL) { | ||||
/* virtual machine is in the process of being created */ | /* virtual machine is in the process of being created */ | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* | /* | ||||
* Get a read lock on the guest memory map by freezing any vcpu. | * Get a read lock on the guest memory map by freezing any vcpu. | ||||
*/ | */ | ||||
error = vcpu_lock_one(sc, VM_MAXCPU - 1); | lastcpu = vm_get_maxcpus(sc->vm) - 1; | ||||
error = vcpu_lock_one(sc, lastcpu); | |||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
gpa = 0; | gpa = 0; | ||||
found = 0; | found = 0; | ||||
while (!found) { | while (!found) { | ||||
error = vm_mmap_getnext(sc->vm, &gpa, &segid, &segoff, &len, | error = vm_mmap_getnext(sc->vm, &gpa, &segid, &segoff, &len, | ||||
NULL, NULL); | NULL, NULL); | ||||
Show All 12 Lines | KASSERT(error == 0 && *objp != NULL, | ||||
("%s: invalid memory segment %d", __func__, segid)); | ("%s: invalid memory segment %d", __func__, segid)); | ||||
if (sysmem) { | if (sysmem) { | ||||
vm_object_reference(*objp); | vm_object_reference(*objp); | ||||
*offset = segoff + (first - gpa); | *offset = segoff + (first - gpa); | ||||
} else { | } else { | ||||
error = EINVAL; | error = EINVAL; | ||||
} | } | ||||
} | } | ||||
vcpu_unlock_one(sc, VM_MAXCPU - 1); | vcpu_unlock_one(sc, lastcpu); | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
vmmdev_destroy(void *arg) | vmmdev_destroy(void *arg) | ||||
{ | { | ||||
struct vmmdev_softc *sc = arg; | struct vmmdev_softc *sc = arg; | ||||
struct devmem_softc *dsc; | struct devmem_softc *dsc; | ||||
▲ Show 20 Lines • Show All 182 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
devmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t len, | devmem_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t len, | ||||
struct vm_object **objp, int nprot) | struct vm_object **objp, int nprot) | ||||
{ | { | ||||
struct devmem_softc *dsc; | struct devmem_softc *dsc; | ||||
vm_ooffset_t first, last; | vm_ooffset_t first, last; | ||||
size_t seglen; | size_t seglen; | ||||
int error; | int error; | ||||
uint16_t lastcpu; | |||||
bool sysmem; | bool sysmem; | ||||
dsc = cdev->si_drv1; | dsc = cdev->si_drv1; | ||||
if (dsc == NULL) { | if (dsc == NULL) { | ||||
/* 'cdev' has been created but is not ready for use */ | /* 'cdev' has been created but is not ready for use */ | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
first = *offset; | first = *offset; | ||||
last = *offset + len; | last = *offset + len; | ||||
if ((nprot & PROT_EXEC) || first < 0 || first >= last) | if ((nprot & PROT_EXEC) || first < 0 || first >= last) | ||||
return (EINVAL); | return (EINVAL); | ||||
error = vcpu_lock_one(dsc->sc, VM_MAXCPU - 1); | lastcpu = vm_get_maxcpus(dsc->sc->vm) - 1; | ||||
error = vcpu_lock_one(dsc->sc, lastcpu); | |||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
error = vm_get_memseg(dsc->sc->vm, dsc->segid, &seglen, &sysmem, objp); | error = vm_get_memseg(dsc->sc->vm, dsc->segid, &seglen, &sysmem, objp); | ||||
KASSERT(error == 0 && !sysmem && *objp != NULL, | KASSERT(error == 0 && !sysmem && *objp != NULL, | ||||
("%s: invalid devmem segment %d", __func__, dsc->segid)); | ("%s: invalid devmem segment %d", __func__, dsc->segid)); | ||||
vcpu_unlock_one(dsc->sc, VM_MAXCPU - 1); | vcpu_unlock_one(dsc->sc, lastcpu); | ||||
if (seglen >= last) { | if (seglen >= last) { | ||||
vm_object_reference(*objp); | vm_object_reference(*objp); | ||||
return (0); | return (0); | ||||
} else { | } else { | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 54 Lines • Show Last 20 Lines |