x86/insn-eval: Introduce insn_decode_mmio()
[linux-2.6-microblaze.git] / arch / x86 / lib / insn-eval.c
index eb3ccff..53e57ef 100644 (file)
@@ -37,8 +37,6 @@ enum reg_type {
  */
 static bool is_string_insn(struct insn *insn)
 {
-       insn_get_opcode(insn);
-
        /* All string instructions have a 1-byte opcode. */
        if (insn->opcode.nbytes != 1)
                return false;
@@ -850,6 +848,26 @@ int insn_get_modrm_reg_off(struct insn *insn, struct pt_regs *regs)
        return get_reg_offset(insn, regs, REG_TYPE_REG);
 }
 
+/**
+ * insn_get_modrm_reg_ptr() - Obtain register pointer based on ModRM byte
+ * @insn:      Instruction containing the ModRM byte
+ * @regs:      Register values as seen when entering kernel mode
+ *
+ * Returns:
+ *
+ * The register indicated by the reg part of the ModRM byte.
+ * The register is obtained as a pointer within pt_regs.
+ */
+unsigned long *insn_get_modrm_reg_ptr(struct insn *insn, struct pt_regs *regs)
+{
+       int offset;
+
+       offset = insn_get_modrm_reg_off(insn, regs);
+       if (offset < 0)
+               return NULL;
+       return (void *)regs + offset;
+}
+
 /**
  * get_seg_base_limit() - obtain base address and limit of a segment
  * @insn:      Instruction. Must be valid.
@@ -1405,6 +1423,9 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
        if (!insn || !regs)
                return (void __user *)-1L;
 
+       if (insn_get_opcode(insn))
+               return (void __user *)-1L;
+
        switch (insn->addr_bytes) {
        case 2:
                return get_addr_ref_16(insn, regs);
@@ -1539,3 +1560,87 @@ bool insn_decode_from_regs(struct insn *insn, struct pt_regs *regs,
 
        return true;
 }
+
+/**
+ * insn_decode_mmio() - Decode a MMIO instruction
+ * @insn:      Structure to store decoded instruction
+ * @bytes:     Returns size of memory operand
+ *
+ * Decodes instruction that used for Memory-mapped I/O.
+ *
+ * Returns:
+ *
+ * Type of the instruction. Size of the memory operand is stored in
+ * @bytes. If decode failed, MMIO_DECODE_FAILED returned.
+ */
+enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
+{
+       enum mmio_type type = MMIO_DECODE_FAILED;
+
+       *bytes = 0;
+
+       if (insn_get_opcode(insn))
+               return MMIO_DECODE_FAILED;
+
+       switch (insn->opcode.bytes[0]) {
+       case 0x88: /* MOV m8,r8 */
+               *bytes = 1;
+               fallthrough;
+       case 0x89: /* MOV m16/m32/m64, r16/m32/m64 */
+               if (!*bytes)
+                       *bytes = insn->opnd_bytes;
+               type = MMIO_WRITE;
+               break;
+
+       case 0xc6: /* MOV m8, imm8 */
+               *bytes = 1;
+               fallthrough;
+       case 0xc7: /* MOV m16/m32/m64, imm16/imm32/imm64 */
+               if (!*bytes)
+                       *bytes = insn->opnd_bytes;
+               type = MMIO_WRITE_IMM;
+               break;
+
+       case 0x8a: /* MOV r8, m8 */
+               *bytes = 1;
+               fallthrough;
+       case 0x8b: /* MOV r16/r32/r64, m16/m32/m64 */
+               if (!*bytes)
+                       *bytes = insn->opnd_bytes;
+               type = MMIO_READ;
+               break;
+
+       case 0xa4: /* MOVS m8, m8 */
+               *bytes = 1;
+               fallthrough;
+       case 0xa5: /* MOVS m16/m32/m64, m16/m32/m64 */
+               if (!*bytes)
+                       *bytes = insn->opnd_bytes;
+               type = MMIO_MOVS;
+               break;
+
+       case 0x0f: /* Two-byte instruction */
+               switch (insn->opcode.bytes[1]) {
+               case 0xb6: /* MOVZX r16/r32/r64, m8 */
+                       *bytes = 1;
+                       fallthrough;
+               case 0xb7: /* MOVZX r32/r64, m16 */
+                       if (!*bytes)
+                               *bytes = 2;
+                       type = MMIO_READ_ZERO_EXTEND;
+                       break;
+
+               case 0xbe: /* MOVSX r16/r32/r64, m8 */
+                       *bytes = 1;
+                       fallthrough;
+               case 0xbf: /* MOVSX r32/r64, m16 */
+                       if (!*bytes)
+                               *bytes = 2;
+                       type = MMIO_READ_SIGN_EXTEND;
+                       break;
+               }
+               break;
+       }
+
+       return type;
+}