Author: neel
Date: Wed May  6 16:25:20 2015
New Revision: 282558
URL: https://svnweb.freebsd.org/changeset/base/282558

Log:
  Deprecate the 3-way return values from vm_gla2gpa() and vm_copy_setup().
  
  Prior to this change both functions returned 0 for success, -1 for failure
  and +1 to indicate that an exception was injected into the guest.
  
  The numerical value of ERESTART also happens to be -1 so when these functions
  returned -1 it had to be translated to a positive errno value to prevent the
  VM_RUN ioctl from being inadvertently restarted. This made it easy to 
introduce
  bugs when writing emulation code.
  
  Fix this by adding an 'int *guest_fault' parameter and setting it to '1' if
  an exception was delivered to the guest. The return value is 0 or EFAULT so
  no additional translation is needed.
  
  Reviewed by:  tychon
  MFC after:    2 weeks
  Differential Revision:        https://reviews.freebsd.org/D2428

Modified:
  head/lib/libvmmapi/vmmapi.c
  head/lib/libvmmapi/vmmapi.h
  head/sys/amd64/include/vmm.h
  head/sys/amd64/include/vmm_instruction_emul.h
  head/sys/amd64/vmm/vmm.c
  head/sys/amd64/vmm/vmm_dev.c
  head/sys/amd64/vmm/vmm_instruction_emul.c
  head/usr.sbin/bhyve/inout.c
  head/usr.sbin/bhyve/task_switch.c

Modified: head/lib/libvmmapi/vmmapi.c
==============================================================================
--- head/lib/libvmmapi/vmmapi.c Wed May  6 16:21:12 2015        (r282557)
+++ head/lib/libvmmapi/vmmapi.c Wed May  6 16:25:20 2015        (r282558)
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
 #include <machine/specialreg.h>
 #include <machine/param.h>
 
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
@@ -958,9 +959,9 @@ vm_get_hpet_capabilities(struct vmctx *c
        return (error);
 }
 
-static int
-gla2gpa(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
-    uint64_t gla, int prot, int *fault, uint64_t *gpa)
+int
+vm_gla2gpa(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
+    uint64_t gla, int prot, uint64_t *gpa, int *fault)
 {
        struct vm_gla2gpa gg;
        int error;
@@ -979,29 +980,18 @@ gla2gpa(struct vmctx *ctx, int vcpu, str
        return (error);
 }
 
-int
-vm_gla2gpa(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
-    uint64_t gla, int prot, uint64_t *gpa)
-{
-       int error, fault;
-
-       error = gla2gpa(ctx, vcpu, paging, gla, prot, &fault, gpa);
-       if (fault)
-               error = fault;
-       return (error);
-}
-
 #ifndef min
 #define        min(a,b)        (((a) < (b)) ? (a) : (b))
 #endif
 
 int
 vm_copy_setup(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
-    uint64_t gla, size_t len, int prot, struct iovec *iov, int iovcnt)
+    uint64_t gla, size_t len, int prot, struct iovec *iov, int iovcnt,
+    int *fault)
 {
        void *va;
        uint64_t gpa;
-       int error, fault, i, n, off;
+       int error, i, n, off;
 
        for (i = 0; i < iovcnt; i++) {
                iov[i].iov_base = 0;
@@ -1010,18 +1000,16 @@ vm_copy_setup(struct vmctx *ctx, int vcp
 
        while (len) {
                assert(iovcnt > 0);
-               error = gla2gpa(ctx, vcpu, paging, gla, prot, &fault, &gpa);
-               if (error)
-                       return (-1);
-               if (fault)
-                       return (1);
+               error = vm_gla2gpa(ctx, vcpu, paging, gla, prot, &gpa, fault);
+               if (error || *fault)
+                       return (error);
 
                off = gpa & PAGE_MASK;
                n = min(len, PAGE_SIZE - off);
 
                va = vm_map_gpa(ctx, gpa, n);
                if (va == NULL)
-                       return (-1);
+                       return (EFAULT);
 
                iov->iov_base = va;
                iov->iov_len = n;

Modified: head/lib/libvmmapi/vmmapi.h
==============================================================================
--- head/lib/libvmmapi/vmmapi.h Wed May  6 16:21:12 2015        (r282557)
+++ head/lib/libvmmapi/vmmapi.h Wed May  6 16:25:20 2015        (r282558)
@@ -64,7 +64,7 @@ int   vm_setup_memory(struct vmctx *ctx, s
 void   *vm_map_gpa(struct vmctx *ctx, vm_paddr_t gaddr, size_t len);
 int    vm_get_gpa_pmap(struct vmctx *, uint64_t gpa, uint64_t *pte, int *num);
 int    vm_gla2gpa(struct vmctx *, int vcpuid, struct vm_guest_paging *paging,
-                  uint64_t gla, int prot, uint64_t *gpa);
+                  uint64_t gla, int prot, uint64_t *gpa, int *fault);
 uint32_t vm_get_lowmem_limit(struct vmctx *ctx);
 void   vm_set_lowmem_limit(struct vmctx *ctx, uint32_t limit);
 void   vm_set_memflags(struct vmctx *ctx, int flags);
@@ -131,10 +131,15 @@ int       vm_get_hpet_capabilities(struct vmct
 /*
  * Translate the GLA range [gla,gla+len) into GPA segments in 'iov'.
  * The 'iovcnt' should be big enough to accomodate all GPA segments.
- * Returns 0 on success, 1 on a guest fault condition and -1 otherwise.
+ *
+ * retval      fault           Interpretation
+ *   0           0             Success
+ *   0           1             An exception was injected into the guest
+ * EFAULT       N/A            Error
  */
 int    vm_copy_setup(struct vmctx *ctx, int vcpu, struct vm_guest_paging *pg,
-           uint64_t gla, size_t len, int prot, struct iovec *iov, int iovcnt);
+           uint64_t gla, size_t len, int prot, struct iovec *iov, int iovcnt,
+           int *fault);
 void   vm_copyin(struct vmctx *ctx, int vcpu, struct iovec *guest_iov,
            void *host_dst, size_t len);
 void   vm_copyout(struct vmctx *ctx, int vcpu, const void *host_src,

Modified: head/sys/amd64/include/vmm.h
==============================================================================
--- head/sys/amd64/include/vmm.h        Wed May  6 16:21:12 2015        
(r282557)
+++ head/sys/amd64/include/vmm.h        Wed May  6 16:25:20 2015        
(r282558)
@@ -345,9 +345,10 @@ struct vm_copyinfo {
  * at 'gla' and 'len' bytes long. The 'prot' should be set to PROT_READ for
  * a copyin or PROT_WRITE for a copyout. 
  *
- * Returns 0 on success.
- * Returns 1 if an exception was injected into the guest.
- * Returns -1 otherwise.
+ * retval      is_fault        Intepretation
+ *   0            0            Success
+ *   0            1            An exception was injected into the guest
+ * EFAULT        N/A           Unrecoverable error
  *
  * The 'copyinfo[]' can be passed to 'vm_copyin()' or 'vm_copyout()' only if
  * the return value is 0. The 'copyinfo[]' resources should be freed by calling
@@ -355,7 +356,7 @@ struct vm_copyinfo {
  */
 int vm_copy_setup(struct vm *vm, int vcpuid, struct vm_guest_paging *paging,
     uint64_t gla, size_t len, int prot, struct vm_copyinfo *copyinfo,
-    int num_copyinfo);
+    int num_copyinfo, int *is_fault);
 void vm_copy_teardown(struct vm *vm, int vcpuid, struct vm_copyinfo *copyinfo,
     int num_copyinfo);
 void vm_copyin(struct vm *vm, int vcpuid, struct vm_copyinfo *copyinfo,

Modified: head/sys/amd64/include/vmm_instruction_emul.h
==============================================================================
--- head/sys/amd64/include/vmm_instruction_emul.h       Wed May  6 16:21:12 
2015        (r282557)
+++ head/sys/amd64/include/vmm_instruction_emul.h       Wed May  6 16:25:20 
2015        (r282558)
@@ -81,17 +81,19 @@ int vie_calculate_gla(enum vm_cpu_mode c
  */
 int vmm_fetch_instruction(struct vm *vm, int cpuid,
                          struct vm_guest_paging *guest_paging,
-                         uint64_t rip, int inst_length, struct vie *vie);
+                         uint64_t rip, int inst_length, struct vie *vie,
+                         int *is_fault);
 
 /*
  * Translate the guest linear address 'gla' to a guest physical address.
  *
- * Returns 0 on success and '*gpa' contains the result of the translation.
- * Returns 1 if an exception was injected into the guest.
- * Returns -1 otherwise.
+ * retval      is_fault        Interpretation
+ *   0            0            'gpa' contains result of the translation
+ *   0            1            An exception was injected into the guest
+ * EFAULT        N/A           An unrecoverable hypervisor error occurred
  */
 int vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging,
-    uint64_t gla, int prot, uint64_t *gpa);
+    uint64_t gla, int prot, uint64_t *gpa, int *is_fault);
 
 void vie_init(struct vie *vie, const char *inst_bytes, int inst_length);
 

Modified: head/sys/amd64/vmm/vmm.c
==============================================================================
--- head/sys/amd64/vmm/vmm.c    Wed May  6 16:21:12 2015        (r282557)
+++ head/sys/amd64/vmm/vmm.c    Wed May  6 16:25:20 2015        (r282558)
@@ -1256,7 +1256,7 @@ vm_handle_inst_emul(struct vm *vm, int v
        mem_region_read_t mread;
        mem_region_write_t mwrite;
        enum vm_cpu_mode cpu_mode;
-       int cs_d, error, length;
+       int cs_d, error, fault, length;
 
        vcpu = &vm->vcpu[vcpuid];
        vme = &vcpu->exitinfo;
@@ -1279,19 +1279,15 @@ vm_handle_inst_emul(struct vm *vm, int v
                 */
                length = vme->inst_length ? vme->inst_length : VIE_INST_SIZE;
                error = vmm_fetch_instruction(vm, vcpuid, paging, vme->rip +
-                   cs_base, length, vie);
+                   cs_base, length, vie, &fault);
        } else {
                /*
                 * The instruction bytes have already been copied into 'vie'
                 */
-               error = 0;
+               error = fault = 0;
        }
-       if (error == 1)
-               return (0);             /* Resume guest to handle page fault */
-       else if (error == -1)
-               return (EFAULT);
-       else if (error != 0)
-               panic("%s: vmm_fetch_instruction error %d", __func__, error);
+       if (error || fault)
+               return (error);
 
        if (vmm_decode_instruction(vm, vcpuid, gla, cpu_mode, cs_d, vie) != 0) {
                VCPU_CTR1(vm, vcpuid, "Error decoding instruction at %#lx",
@@ -2323,7 +2319,7 @@ vm_copy_teardown(struct vm *vm, int vcpu
 int
 vm_copy_setup(struct vm *vm, int vcpuid, struct vm_guest_paging *paging,
     uint64_t gla, size_t len, int prot, struct vm_copyinfo *copyinfo,
-    int num_copyinfo)
+    int num_copyinfo, int *fault)
 {
        int error, idx, nused;
        size_t n, off, remaining;
@@ -2336,8 +2332,8 @@ vm_copy_setup(struct vm *vm, int vcpuid,
        remaining = len;
        while (remaining > 0) {
                KASSERT(nused < num_copyinfo, ("insufficient vm_copyinfo"));
-               error = vm_gla2gpa(vm, vcpuid, paging, gla, prot, &gpa);
-               if (error)
+               error = vm_gla2gpa(vm, vcpuid, paging, gla, prot, &gpa, fault);
+               if (error || *fault)
                        return (error);
                off = gpa & PAGE_MASK;
                n = min(remaining, PAGE_SIZE - off);
@@ -2359,8 +2355,9 @@ vm_copy_setup(struct vm *vm, int vcpuid,
 
        if (idx != nused) {
                vm_copy_teardown(vm, vcpuid, copyinfo, num_copyinfo);
-               return (-1);
+               return (EFAULT);
        } else {
+               *fault = 0;
                return (0);
        }
 }

Modified: head/sys/amd64/vmm/vmm_dev.c
==============================================================================
--- head/sys/amd64/vmm/vmm_dev.c        Wed May  6 16:21:12 2015        
(r282557)
+++ head/sys/amd64/vmm/vmm_dev.c        Wed May  6 16:25:20 2015        
(r282558)
@@ -441,19 +441,9 @@ vmmdev_ioctl(struct cdev *cdev, u_long c
                CTASSERT(PROT_EXEC == VM_PROT_EXECUTE);
                gg = (struct vm_gla2gpa *)data;
                error = vm_gla2gpa(sc->vm, gg->vcpuid, &gg->paging, gg->gla,
-                   gg->prot, &gg->gpa);
-               KASSERT(error == 0 || error == 1 || error == -1,
+                   gg->prot, &gg->gpa, &gg->fault);
+               KASSERT(error == 0 || error == EFAULT,
                    ("%s: vm_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;
        }
        case VM_ACTIVATE_CPU:

Modified: head/sys/amd64/vmm/vmm_instruction_emul.c
==============================================================================
--- head/sys/amd64/vmm/vmm_instruction_emul.c   Wed May  6 16:21:12 2015        
(r282557)
+++ head/sys/amd64/vmm/vmm_instruction_emul.c   Wed May  6 16:25:20 2015        
(r282558)
@@ -597,13 +597,11 @@ emulate_movx(void *vm, int vcpuid, uint6
 
 /*
  * Helper function to calculate and validate a linear address.
- *
- * Returns 0 on success and 1 if an exception was injected into the guest.
  */
 static int
 get_gla(void *vm, int vcpuid, struct vie *vie, struct vm_guest_paging *paging,
     int opsize, int addrsize, int prot, enum vm_reg_name seg,
-    enum vm_reg_name gpr, uint64_t *gla)
+    enum vm_reg_name gpr, uint64_t *gla, int *fault)
 {
        struct seg_desc desc;
        uint64_t cr0, val, rflags;
@@ -629,7 +627,7 @@ get_gla(void *vm, int vcpuid, struct vie
                        vm_inject_ss(vm, vcpuid, 0);
                else
                        vm_inject_gp(vm, vcpuid);
-               return (1);
+               goto guest_fault;
        }
 
        if (vie_canonical_check(paging->cpu_mode, *gla)) {
@@ -637,14 +635,19 @@ get_gla(void *vm, int vcpuid, struct vie
                        vm_inject_ss(vm, vcpuid, 0);
                else
                        vm_inject_gp(vm, vcpuid);
-               return (1);
+               goto guest_fault;
        }
 
        if (vie_alignment_check(paging->cpl, opsize, cr0, rflags, *gla)) {
                vm_inject_ac(vm, vcpuid, 0);
-               return (1);
+               goto guest_fault;
        }
 
+       *fault = 0;
+       return (0);
+
+guest_fault:
+       *fault = 1;
        return (0);
 }
 
@@ -660,7 +663,7 @@ emulate_movs(void *vm, int vcpuid, uint6
 #endif
        uint64_t dstaddr, srcaddr, dstgpa, srcgpa, val;
        uint64_t rcx, rdi, rsi, rflags;
-       int error, opsize, seg, repeat;
+       int error, fault, opsize, seg, repeat;
 
        opsize = (vie->op.op_byte == 0xA4) ? 1 : vie->opsize;
        val = 0;
@@ -683,8 +686,10 @@ emulate_movs(void *vm, int vcpuid, uint6
                 * The count register is %rcx, %ecx or %cx depending on the
                 * address size of the instruction.
                 */
-               if ((rcx & vie_size2mask(vie->addrsize)) == 0)
-                       return (0);
+               if ((rcx & vie_size2mask(vie->addrsize)) == 0) {
+                       error = 0;
+                       goto done;
+               }
        }
 
        /*
@@ -705,13 +710,16 @@ emulate_movs(void *vm, int vcpuid, uint6
 
        seg = vie->segment_override ? vie->segment_register : VM_REG_GUEST_DS;
        error = get_gla(vm, vcpuid, vie, paging, opsize, vie->addrsize,
-           PROT_READ, seg, VM_REG_GUEST_RSI, &srcaddr);
-       if (error)
+           PROT_READ, seg, VM_REG_GUEST_RSI, &srcaddr, &fault);
+       if (error || fault)
                goto done;
 
        error = vm_copy_setup(vm, vcpuid, paging, srcaddr, opsize, PROT_READ,
-           copyinfo, nitems(copyinfo));
+           copyinfo, nitems(copyinfo), &fault);
        if (error == 0) {
+               if (fault)
+                       goto done;      /* Resume guest to handle fault */
+
                /*
                 * case (2): read from system memory and write to mmio.
                 */
@@ -720,11 +728,6 @@ emulate_movs(void *vm, int vcpuid, uint6
                error = memwrite(vm, vcpuid, gpa, val, opsize, arg);
                if (error)
                        goto done;
-       } else if (error > 0) {
-               /*
-                * Resume guest execution to handle fault.
-                */
-               goto done;
        } else {
                /*
                 * 'vm_copy_setup()' is expected to fail for cases (3) and (4)
@@ -732,13 +735,17 @@ emulate_movs(void *vm, int vcpuid, uint6
                 */
 
                error = get_gla(vm, vcpuid, vie, paging, opsize, vie->addrsize,
-                   PROT_WRITE, VM_REG_GUEST_ES, VM_REG_GUEST_RDI, &dstaddr);
-               if (error)
+                   PROT_WRITE, VM_REG_GUEST_ES, VM_REG_GUEST_RDI, &dstaddr,
+                   &fault);
+               if (error || fault)
                        goto done;
 
                error = vm_copy_setup(vm, vcpuid, paging, dstaddr, opsize,
-                   PROT_WRITE, copyinfo, nitems(copyinfo));
+                   PROT_WRITE, copyinfo, nitems(copyinfo), &fault);
                if (error == 0) {
+                       if (fault)
+                               goto done;    /* Resume guest to handle fault */
+
                        /*
                         * case (3): read from MMIO and write to system memory.
                         *
@@ -754,27 +761,29 @@ emulate_movs(void *vm, int vcpuid, uint6
 
                        vm_copyout(vm, vcpuid, &val, copyinfo, opsize);
                        vm_copy_teardown(vm, vcpuid, copyinfo, 
nitems(copyinfo));
-               } else if (error > 0) {
-                       /*
-                        * Resume guest execution to handle fault.
-                        */
-                       goto done;
                } else {
                        /*
                         * Case (4): read from and write to mmio.
+                        *
+                        * Commit to the MMIO read/write (with potential
+                        * side-effects) only after we are sure that the
+                        * instruction is not going to be restarted due
+                        * to address translation faults.
                         */
                        error = vm_gla2gpa(vm, vcpuid, paging, srcaddr,
-                           PROT_READ, &srcgpa);
-                       if (error)
-                               goto done;
-                       error = memread(vm, vcpuid, srcgpa, &val, opsize, arg);
-                       if (error)
+                           PROT_READ, &srcgpa, &fault);
+                       if (error || fault)
                                goto done;
 
                        error = vm_gla2gpa(vm, vcpuid, paging, dstaddr,
-                          PROT_WRITE, &dstgpa);
+                          PROT_WRITE, &dstgpa, &fault);
+                       if (error || fault)
+                               goto done;
+
+                       error = memread(vm, vcpuid, srcgpa, &val, opsize, arg);
                        if (error)
                                goto done;
+
                        error = memwrite(vm, vcpuid, dstgpa, val, opsize, arg);
                        if (error)
                                goto done;
@@ -819,10 +828,9 @@ emulate_movs(void *vm, int vcpuid, uint6
                        vm_restart_instruction(vm, vcpuid);
        }
 done:
-       if (error < 0)
-               return (EFAULT);
-       else
-               return (0);
+       KASSERT(error == 0 || error == EFAULT, ("%s: unexpected error %d",
+           __func__, error));
+       return (error);
 }
 
 static int
@@ -1185,7 +1193,7 @@ emulate_stack_op(void *vm, int vcpuid, u
 #endif
        struct seg_desc ss_desc;
        uint64_t cr0, rflags, rsp, stack_gla, val;
-       int error, size, stackaddrsize, pushop;
+       int error, fault, size, stackaddrsize, pushop;
 
        val = 0;
        size = vie->opsize;
@@ -1251,18 +1259,10 @@ emulate_stack_op(void *vm, int vcpuid, u
        }
 
        error = vm_copy_setup(vm, vcpuid, paging, stack_gla, size,
-           pushop ? PROT_WRITE : PROT_READ, copyinfo, nitems(copyinfo));
-       if (error == -1) {
-               /*
-                * XXX cannot return a negative error value here because it
-                * ends up being the return value of the VM_RUN() ioctl and
-                * is interpreted as a pseudo-error (for e.g. ERESTART).
-                */
-               return (EFAULT);
-       } else if (error == 1) {
-               /* Resume guest execution to handle page fault */
-               return (0);
-       }
+           pushop ? PROT_WRITE : PROT_READ, copyinfo, nitems(copyinfo),
+           &fault);
+       if (error || fault)
+               return (error);
 
        if (pushop) {
                error = memread(vm, vcpuid, mmio_gpa, &val, size, arg);
@@ -1672,7 +1672,7 @@ ptp_hold(struct vm *vm, vm_paddr_t ptpph
 
 int
 vm_gla2gpa(struct vm *vm, int vcpuid, struct vm_guest_paging *paging,
-    uint64_t gla, int prot, uint64_t *gpa)
+    uint64_t gla, int prot, uint64_t *gpa, int *guest_fault)
 {
        int nlevels, pfcode, ptpshift, ptpindex, retval, usermode, writable;
        u_int retries;
@@ -1680,6 +1680,8 @@ vm_gla2gpa(struct vm *vm, int vcpuid, st
        uint32_t *ptpbase32, pte32;
        void *cookie;
 
+       *guest_fault = 0;
+
        usermode = (paging->cpl == 3 ? 1 : 0);
        writable = prot & VM_PROT_WRITE;
        cookie = NULL;
@@ -1842,18 +1844,20 @@ restart:
        *gpa = pte | (gla & (pgsize - 1));
 done:
        ptp_release(&cookie);
+       KASSERT(retval == 0 || retval == EFAULT, ("%s: unexpected retval %d",
+           __func__, retval));
        return (retval);
 error:
-       retval = -1;
+       retval = EFAULT;
        goto done;
 fault:
-       retval = 1;
+       *guest_fault = 1;
        goto done;
 }
 
 int
 vmm_fetch_instruction(struct vm *vm, int vcpuid, struct vm_guest_paging 
*paging,
-    uint64_t rip, int inst_length, struct vie *vie)
+    uint64_t rip, int inst_length, struct vie *vie, int *faultptr)
 {
        struct vm_copyinfo copyinfo[2];
        int error, prot;
@@ -1863,13 +1867,14 @@ vmm_fetch_instruction(struct vm *vm, int
 
        prot = PROT_READ | PROT_EXEC;
        error = vm_copy_setup(vm, vcpuid, paging, rip, inst_length, prot,
-           copyinfo, nitems(copyinfo));
-       if (error == 0) {
-               vm_copyin(vm, vcpuid, copyinfo, vie->inst, inst_length);
-               vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
-               vie->num_valid = inst_length;
-       }
-       return (error);
+           copyinfo, nitems(copyinfo), faultptr);
+       if (error || *faultptr)
+               return (error);
+
+       vm_copyin(vm, vcpuid, copyinfo, vie->inst, inst_length);
+       vm_copy_teardown(vm, vcpuid, copyinfo, nitems(copyinfo));
+       vie->num_valid = inst_length;
+       return (0);
 }
 
 static int

Modified: head/usr.sbin/bhyve/inout.c
==============================================================================
--- head/usr.sbin/bhyve/inout.c Wed May  6 16:21:12 2015        (r282557)
+++ head/usr.sbin/bhyve/inout.c Wed May  6 16:25:20 2015        (r282558)
@@ -107,7 +107,7 @@ emulate_inout(struct vmctx *ctx, int vcp
        uint32_t eax, val;
        inout_func_t handler;
        void *arg;
-       int error, retval;
+       int error, fault, retval;
        enum vm_reg_name idxreg;
        uint64_t gla, index, iterations, count;
        struct vm_inout_str *vis;
@@ -163,11 +163,11 @@ emulate_inout(struct vmctx *ctx, int vcp
                        }
 
                        error = vm_copy_setup(ctx, vcpu, &vis->paging, gla,
-                           bytes, prot, iov, nitems(iov));
-                       if (error == -1) {
+                           bytes, prot, iov, nitems(iov), &fault);
+                       if (error) {
                                retval = -1;  /* Unrecoverable error */
                                break;
-                       } else if (error == 1) {
+                       } else if (fault) {
                                retval = 0;  /* Resume guest to handle fault */
                                break;
                        }

Modified: head/usr.sbin/bhyve/task_switch.c
==============================================================================
--- head/usr.sbin/bhyve/task_switch.c   Wed May  6 16:21:12 2015        
(r282557)
+++ head/usr.sbin/bhyve/task_switch.c   Wed May  6 16:25:20 2015        
(r282558)
@@ -202,7 +202,8 @@ desc_table_limit_check(struct vmctx *ctx
  */
 static int
 desc_table_rw(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
-    uint16_t sel, struct user_segment_descriptor *desc, bool doread)
+    uint16_t sel, struct user_segment_descriptor *desc, bool doread,
+    int *faultptr)
 {
        struct iovec iov[2];
        uint64_t base;
@@ -215,28 +216,30 @@ desc_table_rw(struct vmctx *ctx, int vcp
        assert(limit >= SEL_LIMIT(sel));
 
        error = vm_copy_setup(ctx, vcpu, paging, base + SEL_START(sel),
-           sizeof(*desc), doread ? PROT_READ : PROT_WRITE, iov, nitems(iov));
-       if (error == 0) {
-               if (doread)
-                       vm_copyin(ctx, vcpu, iov, desc, sizeof(*desc));
-               else
-                       vm_copyout(ctx, vcpu, desc, iov, sizeof(*desc));
-       }
-       return (error);
+           sizeof(*desc), doread ? PROT_READ : PROT_WRITE, iov, nitems(iov),
+           faultptr);
+       if (error || *faultptr)
+               return (error);
+
+       if (doread)
+               vm_copyin(ctx, vcpu, iov, desc, sizeof(*desc));
+       else
+               vm_copyout(ctx, vcpu, desc, iov, sizeof(*desc));
+       return (0);
 }
 
 static int
 desc_table_read(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
-    uint16_t sel, struct user_segment_descriptor *desc)
+    uint16_t sel, struct user_segment_descriptor *desc, int *faultptr)
 {
-       return (desc_table_rw(ctx, vcpu, paging, sel, desc, true));
+       return (desc_table_rw(ctx, vcpu, paging, sel, desc, true, faultptr));
 }
 
 static int
 desc_table_write(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
-    uint16_t sel, struct user_segment_descriptor *desc)
+    uint16_t sel, struct user_segment_descriptor *desc, int *faultptr)
 {
-       return (desc_table_rw(ctx, vcpu, paging, sel, desc, false));
+       return (desc_table_rw(ctx, vcpu, paging, sel, desc, false, faultptr));
 }
 
 /*
@@ -248,7 +251,7 @@ desc_table_write(struct vmctx *ctx, int 
  */
 static int
 read_tss_descriptor(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts,
-    uint16_t sel, struct user_segment_descriptor *desc)
+    uint16_t sel, struct user_segment_descriptor *desc, int *faultptr)
 {
        struct vm_guest_paging sup_paging;
        int error;
@@ -267,7 +270,7 @@ read_tss_descriptor(struct vmctx *ctx, i
 
        sup_paging = ts->paging;
        sup_paging.cpl = 0;             /* implicit supervisor mode */
-       error = desc_table_read(ctx, vcpu, &sup_paging, sel, desc);
+       error = desc_table_read(ctx, vcpu, &sup_paging, sel, desc, faultptr);
        return (error);
 }
 
@@ -301,14 +304,10 @@ ldt_desc(int sd_type)
 
 /*
  * Validate the descriptor 'seg_desc' associated with 'segment'.
- *
- * Returns 0 on success.
- * Returns 1 if an exception was injected into the guest.
- * Returns -1 otherwise.
  */
 static int
 validate_seg_desc(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts,
-    int segment, struct seg_desc *seg_desc)
+    int segment, struct seg_desc *seg_desc, int *faultptr)
 {
        struct vm_guest_paging sup_paging;
        struct user_segment_descriptor usd;
@@ -369,8 +368,8 @@ validate_seg_desc(struct vmctx *ctx, int
        /* Read the descriptor from the GDT/LDT */
        sup_paging = ts->paging;
        sup_paging.cpl = 0;     /* implicit supervisor mode */
-       error = desc_table_read(ctx, vcpu, &sup_paging, sel, &usd);
-       if (error)
+       error = desc_table_read(ctx, vcpu, &sup_paging, sel, &usd, faultptr);
+       if (error || *faultptr)
                return (error);
 
        /* Verify that the descriptor type is compatible with the segment */
@@ -476,14 +475,10 @@ update_seg_desc(struct vmctx *ctx, int v
 
 /*
  * Update the vcpu registers to reflect the state of the new task.
- *
- * Returns 0 on success.
- * Returns 1 if an exception was injected into the guest.
- * Returns -1 otherwise.
  */
 static int
 tss32_restore(struct vmctx *ctx, int vcpu, struct vm_task_switch *ts,
-    uint16_t ot_sel, struct tss32 *tss, struct iovec *iov)
+    uint16_t ot_sel, struct tss32 *tss, struct iovec *iov, int *faultptr)
 {
        struct seg_desc seg_desc, seg_desc2;
        uint64_t *pdpte, maxphyaddr, reserved;
@@ -565,8 +560,9 @@ tss32_restore(struct vmctx *ctx, int vcp
                vm_copyout(ctx, vcpu, tss, iov, sizeof(*tss));
 
        /* Validate segment descriptors */
-       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_LDTR, &seg_desc);
-       if (error)
+       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_LDTR, &seg_desc,
+           faultptr);
+       if (error || *faultptr)
                return (error);
        update_seg_desc(ctx, vcpu, VM_REG_GUEST_LDTR, &seg_desc);
 
@@ -579,33 +575,40 @@ tss32_restore(struct vmctx *ctx, int vcp
         * VM-entry checks so the guest can handle any exception injected
         * during task switch emulation.
         */
-       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_CS, &seg_desc);
-       if (error)
+       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_CS, &seg_desc,
+           faultptr);
+       if (error || *faultptr)
                return (error);
-       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_SS, &seg_desc2);
-       if (error)
+
+       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_SS, &seg_desc2,
+           faultptr);
+       if (error || *faultptr)
                return (error);
        update_seg_desc(ctx, vcpu, VM_REG_GUEST_CS, &seg_desc);
        update_seg_desc(ctx, vcpu, VM_REG_GUEST_SS, &seg_desc2);
        ts->paging.cpl = tss->tss_cs & SEL_RPL_MASK;
 
-       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_DS, &seg_desc);
-       if (error)
+       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_DS, &seg_desc,
+           faultptr);
+       if (error || *faultptr)
                return (error);
        update_seg_desc(ctx, vcpu, VM_REG_GUEST_DS, &seg_desc);
 
-       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_ES, &seg_desc);
-       if (error)
+       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_ES, &seg_desc,
+           faultptr);
+       if (error || *faultptr)
                return (error);
        update_seg_desc(ctx, vcpu, VM_REG_GUEST_ES, &seg_desc);
 
-       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_FS, &seg_desc);
-       if (error)
+       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_FS, &seg_desc,
+           faultptr);
+       if (error || *faultptr)
                return (error);
        update_seg_desc(ctx, vcpu, VM_REG_GUEST_FS, &seg_desc);
 
-       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_GS, &seg_desc);
-       if (error)
+       error = validate_seg_desc(ctx, vcpu, ts, VM_REG_GUEST_GS, &seg_desc,
+           faultptr);
+       if (error || *faultptr)
                return (error);
        update_seg_desc(ctx, vcpu, VM_REG_GUEST_GS, &seg_desc);
 
@@ -616,14 +619,10 @@ tss32_restore(struct vmctx *ctx, int vcp
  * Push an error code on the stack of the new task. This is needed if the
  * task switch was triggered by a hardware exception that causes an error
  * code to be saved (e.g. #PF).
- *
- * Returns 0 on success.
- * Returns 1 if an exception was injected into the guest.
- * Returns -1 otherwise.
  */
 static int
 push_errcode(struct vmctx *ctx, int vcpu, struct vm_guest_paging *paging,
-    int task_type, uint32_t errcode)
+    int task_type, uint32_t errcode, int *faultptr)
 {
        struct iovec iov[2];
        struct seg_desc seg_desc;
@@ -632,6 +631,8 @@ push_errcode(struct vmctx *ctx, int vcpu
        uint32_t esp;
        uint16_t stacksel;
 
+       *faultptr = 0;
+
        cr0 = GETREG(ctx, vcpu, VM_REG_GUEST_CR0);
        rflags = GETREG(ctx, vcpu, VM_REG_GUEST_RFLAGS);
        stacksel = GETREG(ctx, vcpu, VM_REG_GUEST_SS);
@@ -666,17 +667,19 @@ push_errcode(struct vmctx *ctx, int vcpu
        if (vie_calculate_gla(paging->cpu_mode, VM_REG_GUEST_SS,
            &seg_desc, esp, bytes, stacksize, PROT_WRITE, &gla)) {
                sel_exception(ctx, vcpu, IDT_SS, stacksel, 1);
-               return (1);
+               *faultptr = 1;
+               return (0);
        }
 
        if (vie_alignment_check(paging->cpl, bytes, cr0, rflags, gla)) {
                vm_inject_ac(ctx, vcpu, 1);
-               return (1);
+               *faultptr = 1;
+               return (0);
        }
 
        error = vm_copy_setup(ctx, vcpu, paging, gla, bytes, PROT_WRITE,
-           iov, nitems(iov));
-       if (error)
+           iov, nitems(iov), faultptr);
+       if (error || *faultptr)
                return (error);
 
        vm_copyout(ctx, vcpu, &errcode, iov, bytes);
@@ -687,16 +690,13 @@ push_errcode(struct vmctx *ctx, int vcpu
 /*
  * Evaluate return value from helper functions and potentially return to
  * the VM run loop.
- *  0: success
- * +1: an exception was injected into the guest vcpu
- * -1: unrecoverable/programming error
  */
-#define        CHKERR(x)                                                       
\
+#define        CHKERR(error,fault)                                             
\
        do {                                                            \
-               assert(((x) == 0) || ((x) == 1) || ((x) == -1));        \
-               if ((x) == -1)                                          \
+               assert((error == 0) || (error == EFAULT));              \
+               if (error)                                              \
                        return (VMEXIT_ABORT);                          \
-               else if ((x) == 1)                                      \
+               else if (fault)                                         \
                        return (VMEXIT_CONTINUE);                       \
        } while (0)
 
@@ -711,7 +711,7 @@ vmexit_task_switch(struct vmctx *ctx, st
        struct iovec nt_iov[2], ot_iov[2];
        uint64_t cr0, ot_base;
        uint32_t eip, ot_lim, access;
-       int error, ext, minlimit, nt_type, ot_type, vcpu;
+       int error, ext, fault, minlimit, nt_type, ot_type, vcpu;
        enum task_switch_reason reason;
        uint16_t nt_sel, ot_sel;
 
@@ -739,8 +739,9 @@ vmexit_task_switch(struct vmctx *ctx, st
        sup_paging.cpl = 0;     /* implicit supervisor mode */
 
        /* Fetch the new TSS descriptor */
-       error = read_tss_descriptor(ctx, vcpu, task_switch, nt_sel, &nt_desc);
-       CHKERR(error);
+       error = read_tss_descriptor(ctx, vcpu, task_switch, nt_sel, &nt_desc,
+           &fault);
+       CHKERR(error, fault);
 
        nt = usd_to_seg_desc(&nt_desc);
 
@@ -792,8 +793,8 @@ vmexit_task_switch(struct vmctx *ctx, st
 
        /* Fetch the new TSS */
        error = vm_copy_setup(ctx, vcpu, &sup_paging, nt.base, minlimit + 1,
-           PROT_READ | PROT_WRITE, nt_iov, nitems(nt_iov));
-       CHKERR(error);
+           PROT_READ | PROT_WRITE, nt_iov, nitems(nt_iov), &fault);
+       CHKERR(error, fault);
        vm_copyin(ctx, vcpu, nt_iov, &newtss, minlimit + 1);
 
        /* Get the old TSS selector from the guest's task register */
@@ -818,13 +819,14 @@ vmexit_task_switch(struct vmctx *ctx, st
        assert(ot_type == SDT_SYS386BSY || ot_type == SDT_SYS286BSY);
 
        /* Fetch the old TSS descriptor */
-       error = read_tss_descriptor(ctx, vcpu, task_switch, ot_sel, &ot_desc);
-       CHKERR(error);
+       error = read_tss_descriptor(ctx, vcpu, task_switch, ot_sel, &ot_desc,
+           &fault);
+       CHKERR(error, fault);
 
        /* Get the old TSS */
        error = vm_copy_setup(ctx, vcpu, &sup_paging, ot_base, minlimit + 1,
-           PROT_READ | PROT_WRITE, ot_iov, nitems(ot_iov));
-       CHKERR(error);
+           PROT_READ | PROT_WRITE, ot_iov, nitems(ot_iov), &fault);
+       CHKERR(error, fault);
        vm_copyin(ctx, vcpu, ot_iov, &oldtss, minlimit + 1);
 
        /*
@@ -834,8 +836,8 @@ vmexit_task_switch(struct vmctx *ctx, st
        if (reason == TSR_IRET || reason == TSR_JMP) {
                ot_desc.sd_type &= ~0x2;
                error = desc_table_write(ctx, vcpu, &sup_paging, ot_sel,
-                   &ot_desc);
-               CHKERR(error);
+                   &ot_desc, &fault);
+               CHKERR(error, fault);
        }
 
        if (nt_type == SDT_SYS286BSY || nt_type == SDT_SYS286TSS) {
@@ -853,8 +855,8 @@ vmexit_task_switch(struct vmctx *ctx, st
        if (reason != TSR_IRET) {
                nt_desc.sd_type |= 0x2;
                error = desc_table_write(ctx, vcpu, &sup_paging, nt_sel,
-                   &nt_desc);
-               CHKERR(error);
+                   &nt_desc, &fault);
+               CHKERR(error, fault);
        }
 
        /* Update task register to point at the new TSS */
@@ -877,8 +879,9 @@ vmexit_task_switch(struct vmctx *ctx, st
        assert(error == 0);
 
        /* Load processor state from new TSS */
-       error = tss32_restore(ctx, vcpu, task_switch, ot_sel, &newtss, nt_iov);
-       CHKERR(error);
+       error = tss32_restore(ctx, vcpu, task_switch, ot_sel, &newtss, nt_iov,
+           &fault);
+       CHKERR(error, fault);
 
        /*
         * Section "Interrupt Tasks" in Intel SDM, Vol 3: if an exception
@@ -889,8 +892,8 @@ vmexit_task_switch(struct vmctx *ctx, st
                assert(task_switch->ext);
                assert(task_switch->reason == TSR_IDT_GATE);
                error = push_errcode(ctx, vcpu, &task_switch->paging, nt_type,
-                   task_switch->errcode);
-               CHKERR(error);
+                   task_switch->errcode, &fault);
+               CHKERR(error, fault);
        }
 
        /*
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to