Author: jhb
Date: Fri May  3 21:48:42 2019
New Revision: 347065
URL: https://svnweb.freebsd.org/changeset/base/347065

Log:
  Emulate the "ADD reg, r/m" instruction (opcode 03H).
  
  OVMF's flash variable storage is using add instructions when indexing
  the variable store bootrom location.
  
  Submitted by: D Scott Phillips <d.scott.phill...@intel.com>
  Reviewed by:  rgrimes
  MFC after:    1 week
  Sponsored by: Intel Corporation
  Differential Revision:        https://reviews.freebsd.org/D19975

Modified:
  head/sys/amd64/vmm/vmm_instruction_emul.c

Modified: head/sys/amd64/vmm/vmm_instruction_emul.c
==============================================================================
--- head/sys/amd64/vmm/vmm_instruction_emul.c   Fri May  3 21:46:25 2019        
(r347064)
+++ head/sys/amd64/vmm/vmm_instruction_emul.c   Fri May  3 21:48:42 2019        
(r347065)
@@ -77,6 +77,7 @@ enum {
        VIE_OP_TYPE_STOS,
        VIE_OP_TYPE_BITTEST,
        VIE_OP_TYPE_TWOB_GRP15,
+       VIE_OP_TYPE_ADD,
        VIE_OP_TYPE_LAST
 };
 
@@ -112,6 +113,10 @@ static const struct vie_op two_byte_opcodes[256] = {
 };
 
 static const struct vie_op one_byte_opcodes[256] = {
+       [0x03] = {
+               .op_byte = 0x03,
+               .op_type = VIE_OP_TYPE_ADD,
+       },
        [0x0F] = {
                .op_byte = 0x0F,
                .op_type = VIE_OP_TYPE_TWO_BYTE
@@ -410,6 +415,41 @@ getcc(int opsize, uint64_t x, uint64_t y)
                return (getcc64(x, y));
 }
 
+/*
+ * Macro creation of functions getaddflags{8,16,32,64}
+ */
+#define        GETADDFLAGS(sz)                                                 
\
+static u_long                                                          \
+getaddflags##sz(uint##sz##_t x, uint##sz##_t y)                                
\
+{                                                                      \
+       u_long rflags;                                                  \
+                                                                       \
+       __asm __volatile("add %2,%1; pushfq; popq %0" :                 \
+           "=r" (rflags), "+r" (x) : "m" (y));                         \
+       return (rflags);                                                \
+} struct __hack
+
+GETADDFLAGS(8);
+GETADDFLAGS(16);
+GETADDFLAGS(32);
+GETADDFLAGS(64);
+
+static u_long
+getaddflags(int opsize, uint64_t x, uint64_t y)
+{
+       KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
+           ("getaddflags: invalid operand size %d", opsize));
+
+       if (opsize == 1)
+               return (getaddflags8(x, y));
+       else if (opsize == 2)
+               return (getaddflags16(x, y));
+       else if (opsize == 4)
+               return (getaddflags32(x, y));
+       else
+               return (getaddflags64(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)
@@ -1179,6 +1219,62 @@ emulate_cmp(void *vm, int vcpuid, uint64_t gpa, struct
 }
 
 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)
+{
+       int error, size;
+       uint64_t nval, rflags, rflags2, val1, val2;
+       enum vm_reg_name reg;
+
+       size = vie->opsize;
+       error = EINVAL;
+
+       switch (vie->op.op_byte) {
+       case 0x03:
+               /*
+                * ADD r/m to r and store the result in r
+                *
+                * 03/r            ADD r16, r/m16
+                * 03/r            ADD r32, r/m32
+                * REX.W + 03/r    ADD r64, r/m64
+                */
+
+               /* get the first operand */
+               reg = gpr_map[vie->reg];
+               error = vie_read_register(vm, vcpuid, reg, &val1);
+               if (error)
+                       break;
+
+               /* get the second operand */
+               error = memread(vm, vcpuid, gpa, &val2, size, arg);
+               if (error)
+                       break;
+
+               /* perform the operation and write the result */
+               nval = val1 + val2;
+               error = vie_update_register(vm, vcpuid, reg, nval, size);
+               break;
+       default:
+               break;
+       }
+
+       if (!error) {
+               rflags2 = getaddflags(size, val1, val2);
+               error = vie_read_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
+                   &rflags);
+               if (error)
+                       return (error);
+
+               rflags &= ~RFLAGS_STATUS_BITS;
+               rflags |= rflags2 & RFLAGS_STATUS_BITS;
+               error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS,
+                   rflags, 8);
+       }
+
+       return (error);
+}
+
+static int
 emulate_sub(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
            mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
 {
@@ -1542,6 +1638,10 @@ vmm_emulate_instruction(void *vm, int vcpuid, uint64_t
        case VIE_OP_TYPE_TWOB_GRP15:
                error = emulate_twob_group15(vm, vcpuid, gpa, vie,
                    memread, memwrite, memarg);
+               break;
+       case VIE_OP_TYPE_ADD:
+               error = emulate_add(vm, vcpuid, gpa, vie, memread,
+                   memwrite, memarg);
                break;
        default:
                error = EINVAL;
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to