Merge tag 'x86_seves_for_v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / arch / x86 / lib / insn-eval.c
index 5e69603ff63ffa3c7e0de515d7de8a1f7a179bfd..58f7fb95c7f49cf2f2d6cae191cb1cbbf1bf782f 100644 (file)
@@ -20,6 +20,7 @@
 
 enum reg_type {
        REG_TYPE_RM = 0,
+       REG_TYPE_REG,
        REG_TYPE_INDEX,
        REG_TYPE_BASE,
 };
@@ -52,6 +53,30 @@ static bool is_string_insn(struct insn *insn)
        }
 }
 
+/**
+ * insn_has_rep_prefix() - Determine if instruction has a REP prefix
+ * @insn:      Instruction containing the prefix to inspect
+ *
+ * Returns:
+ *
+ * true if the instruction has a REP prefix, false if not.
+ */
+bool insn_has_rep_prefix(struct insn *insn)
+{
+       int i;
+
+       insn_get_prefixes(insn);
+
+       for (i = 0; i < insn->prefixes.nbytes; i++) {
+               insn_byte_t p = insn->prefixes.bytes[i];
+
+               if (p == 0xf2 || p == 0xf3)
+                       return true;
+       }
+
+       return false;
+}
+
 /**
  * get_seg_reg_override_idx() - obtain segment register override index
  * @insn:      Valid instruction with segment override prefixes
@@ -439,6 +464,13 @@ static int get_reg_offset(struct insn *insn, struct pt_regs *regs,
                        regno += 8;
                break;
 
+       case REG_TYPE_REG:
+               regno = X86_MODRM_REG(insn->modrm.value);
+
+               if (X86_REX_R(insn->rex_prefix.value))
+                       regno += 8;
+               break;
+
        case REG_TYPE_INDEX:
                regno = X86_SIB_INDEX(insn->sib.value);
                if (X86_REX_X(insn->rex_prefix.value))
@@ -807,6 +839,21 @@ int insn_get_modrm_rm_off(struct insn *insn, struct pt_regs *regs)
        return get_reg_offset(insn, regs, REG_TYPE_RM);
 }
 
+/**
+ * insn_get_modrm_reg_off() - Obtain register in reg part of the 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 an offset from the base of pt_regs.
+ */
+int insn_get_modrm_reg_off(struct insn *insn, struct pt_regs *regs)
+{
+       return get_reg_offset(insn, regs, REG_TYPE_REG);
+}
+
 /**
  * get_seg_base_limit() - obtain base address and limit of a segment
  * @insn:      Instruction. Must be valid.
@@ -1367,3 +1414,86 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
                return (void __user *)-1L;
        }
 }
+
+/**
+ * insn_fetch_from_user() - Copy instruction bytes from user-space memory
+ * @regs:      Structure with register values as seen when entering kernel mode
+ * @buf:       Array to store the fetched instruction
+ *
+ * Gets the linear address of the instruction and copies the instruction bytes
+ * to the buf.
+ *
+ * Returns:
+ *
+ * Number of instruction bytes copied.
+ *
+ * 0 if nothing was copied.
+ */
+int insn_fetch_from_user(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])
+{
+       unsigned long seg_base = 0;
+       int not_copied;
+
+       /*
+        * If not in user-space long mode, a custom code segment could be in
+        * use. This is true in protected mode (if the process defined a local
+        * descriptor table), or virtual-8086 mode. In most of the cases
+        * seg_base will be zero as in USER_CS.
+        */
+       if (!user_64bit_mode(regs)) {
+               seg_base = insn_get_seg_base(regs, INAT_SEG_REG_CS);
+               if (seg_base == -1L)
+                       return 0;
+       }
+
+
+       not_copied = copy_from_user(buf, (void __user *)(seg_base + regs->ip),
+                                   MAX_INSN_SIZE);
+
+       return MAX_INSN_SIZE - not_copied;
+}
+
+/**
+ * insn_decode() - Decode an instruction
+ * @insn:      Structure to store decoded instruction
+ * @regs:      Structure with register values as seen when entering kernel mode
+ * @buf:       Buffer containing the instruction bytes
+ * @buf_size:   Number of instruction bytes available in buf
+ *
+ * Decodes the instruction provided in buf and stores the decoding results in
+ * insn. Also determines the correct address and operand sizes.
+ *
+ * Returns:
+ *
+ * True if instruction was decoded, False otherwise.
+ */
+bool insn_decode(struct insn *insn, struct pt_regs *regs,
+                unsigned char buf[MAX_INSN_SIZE], int buf_size)
+{
+       int seg_defs;
+
+       insn_init(insn, buf, buf_size, user_64bit_mode(regs));
+
+       /*
+        * Override the default operand and address sizes with what is specified
+        * in the code segment descriptor. The instruction decoder only sets
+        * the address size it to either 4 or 8 address bytes and does nothing
+        * for the operand bytes. This OK for most of the cases, but we could
+        * have special cases where, for instance, a 16-bit code segment
+        * descriptor is used.
+        * If there is an address override prefix, the instruction decoder
+        * correctly updates these values, even for 16-bit defaults.
+        */
+       seg_defs = insn_get_code_seg_params(regs);
+       if (seg_defs == -EINVAL)
+               return false;
+
+       insn->addr_bytes = INSN_CODE_SEG_ADDR_SZ(seg_defs);
+       insn->opnd_bytes = INSN_CODE_SEG_OPND_SZ(seg_defs);
+
+       insn_get_length(insn);
+       if (buf_size < insn->length)
+               return false;
+
+       return true;
+}