Author: markj
Date: Sun Jul  7 17:31:13 2019
New Revision: 349809
URL: https://svnweb.freebsd.org/changeset/base/349809

Log:
  MFC r349441 (by rgrimes):
  Emulate the "TEST r/m{16,32,64}, imm{16,32,32}" instructions (opcode F7H).
  
  PR:   238794

Modified:
  stable/11/sys/amd64/vmm/vmm_instruction_emul.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/amd64/vmm/vmm_instruction_emul.c
==============================================================================
--- stable/11/sys/amd64/vmm/vmm_instruction_emul.c      Sun Jul  7 17:30:23 
2019        (r349808)
+++ stable/11/sys/amd64/vmm/vmm_instruction_emul.c      Sun Jul  7 17:31:13 
2019        (r349809)
@@ -76,6 +76,7 @@ enum {
        VIE_OP_TYPE_BITTEST,
        VIE_OP_TYPE_TWOB_GRP15,
        VIE_OP_TYPE_ADD,
+       VIE_OP_TYPE_TEST,
        VIE_OP_TYPE_LAST
 };
 
@@ -219,6 +220,12 @@ static const struct vie_op one_byte_opcodes[256] = {
                .op_byte = 0x8F,
                .op_type = VIE_OP_TYPE_POP,
        },
+       [0xF7] = {
+               /* XXX Group 3 extended opcode - not just TEST */
+               .op_byte = 0xF7,
+               .op_type = VIE_OP_TYPE_TEST,
+               .op_flags = VIE_OP_F_IMM,
+       },
        [0xFF] = {
                /* XXX Group 5 extended opcode - not just PUSH */
                .op_byte = 0xFF,
@@ -448,6 +455,41 @@ getaddflags(int opsize, uint64_t x, uint64_t y)
                return (getaddflags64(x, y));
 }
 
+/*
+ * Return the status flags that would result from doing (x & y).
+ */
+#define        GETANDFLAGS(sz)                                                 
\
+static u_long                                                          \
+getandflags##sz(uint##sz##_t x, uint##sz##_t y)                                
\
+{                                                                      \
+       u_long rflags;                                                  \
+                                                                       \
+       __asm __volatile("and %2,%1; pushfq; popq %0" :                 \
+           "=r" (rflags), "+r" (x) : "m" (y));                         \
+       return (rflags);                                                \
+} struct __hack
+
+GETANDFLAGS(8);
+GETANDFLAGS(16);
+GETANDFLAGS(32);
+GETANDFLAGS(64);
+
+static u_long
+getandflags(int opsize, uint64_t x, uint64_t y)
+{
+       KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
+           ("getandflags: invalid operand size %d", opsize));
+
+       if (opsize == 1)
+               return (getandflags8(x, y));
+       else if (opsize == 2)
+               return (getandflags16(x, y));
+       else if (opsize == 4)
+               return (getandflags32(x, y));
+       else
+               return (getandflags64(x, y));
+}
+
 static int
 emulate_mov(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
            mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
@@ -1217,6 +1259,55 @@ emulate_cmp(void *vm, int vcpuid, uint64_t gpa, struct
 }
 
 static int
+emulate_test(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
+    mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
+{
+       int error, size;
+       uint64_t op1, rflags, rflags2;
+
+       size = vie->opsize;
+       error = EINVAL;
+
+       switch (vie->op.op_byte) {
+       case 0xF7:
+               /*
+                * F7 /0                test r/m16, imm16
+                * F7 /0                test r/m32, imm32
+                * REX.W + F7 /0        test r/m64, imm32 sign-extended to 64
+                *
+                * Test mem (ModRM:r/m) with immediate and set status
+                * flags according to the results.  The comparison is
+                * performed by anding the immediate from the first
+                * operand and then setting the status flags.
+                */
+               if ((vie->reg & 7) != 0)
+                       return (EINVAL);
+
+               error = memread(vm, vcpuid, gpa, &op1, size, arg);
+               if (error)
+                       return (error);
+
+               rflags2 = getandflags(size, op1, vie->immediate);
+               break;
+       default:
+               return (EINVAL);
+       }
+       error = vie_read_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
+       if (error)
+               return (error);
+
+       /*
+        * OF and CF are cleared; the SF, ZF and PF flags are set according
+        * to the result; AF is undefined.
+        */
+       rflags &= ~RFLAGS_STATUS_BITS;
+       rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
+
+       error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
+       return (error);
+}
+
+static int
 emulate_add(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
            mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
 {
@@ -1640,6 +1731,10 @@ vmm_emulate_instruction(void *vm, int vcpuid, uint64_t
        case VIE_OP_TYPE_ADD:
                error = emulate_add(vm, vcpuid, gpa, vie, memread,
                    memwrite, memarg);
+               break;
+       case VIE_OP_TYPE_TEST:
+               error = emulate_test(vm, vcpuid, gpa, vie,
+                   memread, memwrite, memarg);
                break;
        default:
                error = EINVAL;
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to