Add libvmmapi functions vm_copyin() and vm_copyout() to copy into and out

of the guest linear address space. These APIs in turn use a new ioctl
'VM_GLA2GPA' to convert the guest linear address to guest physical.

Use the new copyin/copyout APIs when emulating ins/outs instruction in
bhyve(8).
This commit is contained in:
Neel Natu 2014-05-24 23:12:30 +00:00
parent 94831f6d21
commit da11f4aa1d
7 changed files with 154 additions and 37 deletions

View file

@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mman.h>
#include <machine/specialreg.h>
#include <machine/param.h>
#include <stdio.h>
#include <stdlib.h>
@ -937,3 +938,88 @@ vm_get_hpet_capabilities(struct vmctx *ctx, uint32_t *capabilities)
*capabilities = cap.capabilities;
return (error);
}
static int
vm_gla2gpa(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
uint64_t gla, int prot, int *fault, uint64_t *gpa)
{
struct vm_gla2gpa gg;
int error;
bzero(&gg, sizeof(struct vm_gla2gpa));
gg.vcpuid = vcpu;
gg.prot = prot;
gg.gla = gla;
gg.paging = *paging;
error = ioctl(ctx->fd, VM_GLA2GPA, &gg);
if (error == 0) {
*fault = gg.fault;
*gpa = gg.gpa;
}
return (error);
}
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
int
vm_copyin(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
uint64_t gla, void *vp, size_t len)
{
char *dst;
const char *src;
uint64_t gpa;
int error, fault, n, off;
dst = vp;
while (len) {
error = vm_gla2gpa(ctx, vcpu, paging, gla, PROT_READ,
&fault, &gpa);
if (error)
return (-1);
if (fault)
return (1);
off = gpa & PAGE_MASK;
n = min(len, PAGE_SIZE - off);
src = vm_map_gpa(ctx, gpa, n);
bcopy(src, dst, n);
gla += n;
dst += n;
len -= n;
}
return (0);
}
int
vm_copyout(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
const void *vp, uint64_t gla, size_t len)
{
uint64_t gpa;
char *dst;
const char *src;
int error, fault, n, off;
src = vp;
while (len) {
error = vm_gla2gpa(ctx, vcpu, paging, gla, PROT_WRITE,
&fault, &gpa);
if (error)
return (-1);
if (fault)
return (1);
off = gpa & PAGE_MASK;
n = min(len, PAGE_SIZE - off);
dst = vm_map_gpa(ctx, gpa, n);
bcopy(src, dst, n);
gla += n;
src += n;
len -= n;
}
return (0);
}

View file

@ -109,6 +109,11 @@ int vm_set_x2apic_state(struct vmctx *ctx, int vcpu, enum x2apic_state s);
int vm_get_hpet_capabilities(struct vmctx *ctx, uint32_t *capabilities);
int vm_copyin(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
uint64_t gla_src, void *dst, size_t len);
int vm_copyout(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
const void *src, uint64_t gla_dst, size_t len);
/* Reset vcpu register state */
int vcpu_reset(struct vmctx *ctx, int vcpu);

View file

@ -427,7 +427,6 @@ struct vm_inout_str {
enum vm_reg_name seg_name;
struct seg_desc seg_desc;
uint64_t gla; /* may be set to VIE_INVALID_GLA */
uint64_t gpa;
};
struct vm_exit {

View file

@ -168,6 +168,15 @@ struct vm_suspend {
enum vm_suspend_how how;
};
struct vm_gla2gpa {
int vcpuid; /* inputs */
int prot; /* PROT_READ or PROT_WRITE */
uint64_t gla;
struct vm_guest_paging paging;
int fault; /* outputs */
uint64_t gpa;
};
enum {
/* general routines */
IOCNUM_ABIVERS = 0,
@ -180,6 +189,7 @@ enum {
IOCNUM_MAP_MEMORY = 10,
IOCNUM_GET_MEMORY_SEG = 11,
IOCNUM_GET_GPA_PMAP = 12,
IOCNUM_GLA2GPA = 13,
/* register/state accessors */
IOCNUM_SET_REGISTER = 20,
@ -289,4 +299,6 @@ enum {
_IOR('v', IOCNUM_GET_HPET_CAPABILITIES, struct vm_hpet_cap)
#define VM_GET_GPA_PMAP \
_IOWR('v', IOCNUM_GET_GPA_PMAP, struct vm_gpa_pte)
#define VM_GLA2GPA \
_IOWR('v', IOCNUM_GLA2GPA, struct vm_gla2gpa)
#endif

View file

@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include <machine/vmparam.h>
#include <machine/vmm.h>
#include <machine/vmm_instruction_emul.h>
#include <machine/vmm_dev.h>
#include "vmm_lapic.h"
@ -168,6 +169,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
struct vm_x2apic *x2apic;
struct vm_gpa_pte *gpapte;
struct vm_suspend *vmsuspend;
struct vm_gla2gpa *gg;
sc = vmmdev_lookup2(cdev);
if (sc == NULL)
@ -192,6 +194,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
case VM_PPTDEV_MSI:
case VM_PPTDEV_MSIX:
case VM_SET_X2APIC_STATE:
case VM_GLA2GPA:
/*
* XXX fragile, handle with care
* Assumes that the first field of the ioctl data is the vcpu.
@ -415,6 +418,27 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
case VM_GET_HPET_CAPABILITIES:
error = vhpet_getcap((struct vm_hpet_cap *)data);
break;
case VM_GLA2GPA: {
CTASSERT(PROT_READ == VM_PROT_READ);
CTASSERT(PROT_WRITE == VM_PROT_WRITE);
CTASSERT(PROT_EXEC == VM_PROT_EXECUTE);
gg = (struct vm_gla2gpa *)data;
error = vmm_gla2gpa(sc->vm, gg->vcpuid, &gg->paging, gg->gla,
gg->prot, &gg->gpa);
KASSERT(error == 0 || error == 1 || error == -1,
("%s: vmm_gla2gpa unknown error %d", __func__, error));
if (error >= 0) {
/*
* error = 0: the translation was successful
* error = 1: a fault was injected into the guest
*/
gg->fault = error;
error = 0;
} else {
error = EFAULT;
}
break;
}
default:
error = ENOTTY;
break;

View file

@ -145,7 +145,7 @@ emulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu)
{
struct vm_inout_str *vis;
uint64_t gla, index, segbase;
int error, in;
int in;
vis = &vmexit->u.inout_str;
in = vis->inout.in;
@ -197,18 +197,8 @@ emulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu)
}
vis->gla = gla;
error = vmm_gla2gpa(vm, vcpuid, &vis->paging, gla,
in ? VM_PROT_WRITE : VM_PROT_READ, &vis->gpa);
KASSERT(error == 0 || error == 1 || error == -1,
("%s: vmm_gla2gpa unexpected error %d", __func__, error));
if (error == -1) {
return (EFAULT);
} else if (error == 1) {
return (0); /* Resume guest to handle page fault */
} else {
*retu = true;
return (0); /* Return to userspace to finish emulation */
}
*retu = true;
return (0); /* Return to userspace to finish emulation */
}
int

View file

@ -102,14 +102,12 @@ int
emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
{
int addrsize, bytes, flags, in, port, rep;
uint64_t gpa, gpaend;
uint32_t val;
inout_func_t handler;
void *arg;
char *gva;
int error, retval;
enum vm_reg_name idxreg;
uint64_t index, count;
uint64_t gla, index, count;
struct vm_inout_str *vis;
bytes = vmexit->u.inout.bytes;
@ -149,10 +147,6 @@ emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
/* Count register */
count = vis->count & vie_size2mask(addrsize);
gpa = vis->gpa;
gpaend = rounddown(gpa + PAGE_SIZE, PAGE_SIZE);
gva = paddr_guest2host(ctx, gpa, gpaend - gpa);
if (vie_alignment_check(vis->paging.cpl, bytes, vis->cr0,
vis->rflags, vis->gla)) {
error = vm_inject_exception2(ctx, vcpu, IDT_AC, 0);
@ -160,26 +154,34 @@ emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
return (INOUT_RESTART);
}
while (count != 0 && gpa < gpaend) {
/*
* XXX this may not work for unaligned accesses because
* the last access on the page may spill over into the
* adjacent page in the linear address space. This is a
* problem because we don't have a gla2gpa() mapping of
* this adjacent page.
*/
assert(gpaend - gpa >= bytes);
gla = vis->gla;
while (count) {
val = 0;
if (!in)
bcopy(gva, &val, bytes);
if (!in) {
error = vm_copyin(ctx, vcpu, &vis->paging,
gla, &val, bytes);
assert(error == 0 || error == 1 || error == -1);
if (error) {
retval = (error == 1) ? INOUT_RESTART :
INOUT_ERROR;
break;
}
}
retval = handler(ctx, vcpu, in, port, bytes, &val, arg);
if (retval != 0)
break;
if (in)
bcopy(&val, gva, bytes);
if (in) {
error = vm_copyout(ctx, vcpu, &vis->paging,
&val, gla, bytes);
assert(error == 0 || error == 1 || error == -1);
if (error) {
retval = (error == 1) ? INOUT_RESTART :
INOUT_ERROR;
break;
}
}
/* Update index */
if (vis->rflags & PSL_D)
@ -188,8 +190,7 @@ emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict)
index += bytes;
count--;
gva += bytes;
gpa += bytes;
gla += bytes;
}
/* Update index register */