#include <asm/inat.h>
 #include <asm/insn.h>
 #include <asm/insn-eval.h>
+#include <asm/vm86.h>
 
 #undef pr_fmt
 #define pr_fmt(fmt) "insn: " fmt
        }
 }
 
+/**
+ * get_seg_reg_override_idx() - obtain segment register override index
+ * @insn:      Valid instruction with segment override prefixes
+ *
+ * Inspect the instruction prefixes in @insn and find segment overrides, if any.
+ *
+ * Returns:
+ *
+ * A constant identifying the segment register to use, among CS, SS, DS,
+ * ES, FS, or GS. INAT_SEG_REG_DEFAULT is returned if no segment override
+ * prefixes were found.
+ *
+ * -EINVAL in case of error.
+ */
+static int get_seg_reg_override_idx(struct insn *insn)
+{
+       int idx = INAT_SEG_REG_DEFAULT;
+       int num_overrides = 0, i;
+
+       insn_get_prefixes(insn);
+
+       /* Look for any segment override prefixes. */
+       for (i = 0; i < insn->prefixes.nbytes; i++) {
+               insn_attr_t attr;
+
+               attr = inat_get_opcode_attribute(insn->prefixes.bytes[i]);
+               switch (attr) {
+               case INAT_MAKE_PREFIX(INAT_PFX_CS):
+                       idx = INAT_SEG_REG_CS;
+                       num_overrides++;
+                       break;
+               case INAT_MAKE_PREFIX(INAT_PFX_SS):
+                       idx = INAT_SEG_REG_SS;
+                       num_overrides++;
+                       break;
+               case INAT_MAKE_PREFIX(INAT_PFX_DS):
+                       idx = INAT_SEG_REG_DS;
+                       num_overrides++;
+                       break;
+               case INAT_MAKE_PREFIX(INAT_PFX_ES):
+                       idx = INAT_SEG_REG_ES;
+                       num_overrides++;
+                       break;
+               case INAT_MAKE_PREFIX(INAT_PFX_FS):
+                       idx = INAT_SEG_REG_FS;
+                       num_overrides++;
+                       break;
+               case INAT_MAKE_PREFIX(INAT_PFX_GS):
+                       idx = INAT_SEG_REG_GS;
+                       num_overrides++;
+                       break;
+               /* No default action needed. */
+               }
+       }
+
+       /* More than one segment override prefix leads to undefined behavior. */
+       if (num_overrides > 1)
+               return -EINVAL;
+
+       return idx;
+}
+
+/**
+ * check_seg_overrides() - check if segment override prefixes are allowed
+ * @insn:      Valid instruction with segment override prefixes
+ * @regoff:    Operand offset, in pt_regs, for which the check is performed
+ *
+ * For a particular register used in register-indirect addressing, determine if
+ * segment override prefixes can be used. Specifically, no overrides are allowed
+ * for rDI if used with a string instruction.
+ *
+ * Returns:
+ *
+ * True if segment override prefixes can be used with the register indicated
+ * in @regoff. False if otherwise.
+ */
+static bool check_seg_overrides(struct insn *insn, int regoff)
+{
+       if (regoff == offsetof(struct pt_regs, di) && is_string_insn(insn))
+               return false;
+
+       return true;
+}
+
+/**
+ * resolve_default_seg() - resolve default segment register index for an operand
+ * @insn:      Instruction with opcode and address size. Must be valid.
+ * @regs:      Register values as seen when entering kernel mode
+ * @off:       Operand offset, in pt_regs, for which resolution is needed
+ *
+ * Resolve the default segment register index associated with the instruction
+ * operand register indicated by @off. Such index is resolved based on defaults
+ * described in the Intel Software Development Manual.
+ *
+ * Returns:
+ *
+ * If in protected mode, a constant identifying the segment register to use,
+ * among CS, SS, ES or DS. If in long mode, INAT_SEG_REG_IGNORE.
+ *
+ * -EINVAL in case of error.
+ */
+static int resolve_default_seg(struct insn *insn, struct pt_regs *regs, int off)
+{
+       if (user_64bit_mode(regs))
+               return INAT_SEG_REG_IGNORE;
+       /*
+        * Resolve the default segment register as described in Section 3.7.4
+        * of the Intel Software Development Manual Vol. 1:
+        *
+        *  + DS for all references involving r[ABCD]X, and rSI.
+        *  + If used in a string instruction, ES for rDI. Otherwise, DS.
+        *  + AX, CX and DX are not valid register operands in 16-bit address
+        *    encodings but are valid for 32-bit and 64-bit encodings.
+        *  + -EDOM is reserved to identify for cases in which no register
+        *    is used (i.e., displacement-only addressing). Use DS.
+        *  + SS for rSP or rBP.
+        *  + CS for rIP.
+        */
+
+       switch (off) {
+       case offsetof(struct pt_regs, ax):
+       case offsetof(struct pt_regs, cx):
+       case offsetof(struct pt_regs, dx):
+               /* Need insn to verify address size. */
+               if (insn->addr_bytes == 2)
+                       return -EINVAL;
+
+       case -EDOM:
+       case offsetof(struct pt_regs, bx):
+       case offsetof(struct pt_regs, si):
+               return INAT_SEG_REG_DS;
+
+       case offsetof(struct pt_regs, di):
+               if (is_string_insn(insn))
+                       return INAT_SEG_REG_ES;
+               return INAT_SEG_REG_DS;
+
+       case offsetof(struct pt_regs, bp):
+       case offsetof(struct pt_regs, sp):
+               return INAT_SEG_REG_SS;
+
+       case offsetof(struct pt_regs, ip):
+               return INAT_SEG_REG_CS;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+/**
+ * resolve_seg_reg() - obtain segment register index
+ * @insn:      Instruction with operands
+ * @regs:      Register values as seen when entering kernel mode
+ * @regoff:    Operand offset, in pt_regs, used to deterimine segment register
+ *
+ * Determine the segment register associated with the operands and, if
+ * applicable, prefixes and the instruction pointed by @insn.
+ *
+ * The segment register associated to an operand used in register-indirect
+ * addressing depends on:
+ *
+ * a) Whether running in long mode (in such a case segments are ignored, except
+ * if FS or GS are used).
+ *
+ * b) Whether segment override prefixes can be used. Certain instructions and
+ *    registers do not allow override prefixes.
+ *
+ * c) Whether segment overrides prefixes are found in the instruction prefixes.
+ *
+ * d) If there are not segment override prefixes or they cannot be used, the
+ *    default segment register associated with the operand register is used.
+ *
+ * The function checks first if segment override prefixes can be used with the
+ * operand indicated by @regoff. If allowed, obtain such overridden segment
+ * register index. Lastly, if not prefixes were found or cannot be used, resolve
+ * the segment register index to use based on the defaults described in the
+ * Intel documentation. In long mode, all segment register indexes will be
+ * ignored, except if overrides were found for FS or GS. All these operations
+ * are done using helper functions.
+ *
+ * The operand register, @regoff, is represented as the offset from the base of
+ * pt_regs.
+ *
+ * As stated, the main use of this function is to determine the segment register
+ * index based on the instruction, its operands and prefixes. Hence, @insn
+ * must be valid. However, if @regoff indicates rIP, we don't need to inspect
+ * @insn at all as in this case CS is used in all cases. This case is checked
+ * before proceeding further.
+ *
+ * Please note that this function does not return the value in the segment
+ * register (i.e., the segment selector) but our defined index. The segment
+ * selector needs to be obtained using get_segment_selector() and passing the
+ * segment register index resolved by this function.
+ *
+ * Returns:
+ *
+ * An index identifying the segment register to use, among CS, SS, DS,
+ * ES, FS, or GS. INAT_SEG_REG_IGNORE is returned if running in long mode.
+ *
+ * -EINVAL in case of error.
+ */
+static int resolve_seg_reg(struct insn *insn, struct pt_regs *regs, int regoff)
+{
+       int idx;
+
+       /*
+        * In the unlikely event of having to resolve the segment register
+        * index for rIP, do it first. Segment override prefixes should not
+        * be used. Hence, it is not necessary to inspect the instruction,
+        * which may be invalid at this point.
+        */
+       if (regoff == offsetof(struct pt_regs, ip)) {
+               if (user_64bit_mode(regs))
+                       return INAT_SEG_REG_IGNORE;
+               else
+                       return INAT_SEG_REG_CS;
+       }
+
+       if (!insn)
+               return -EINVAL;
+
+       if (!check_seg_overrides(insn, regoff))
+               return resolve_default_seg(insn, regs, regoff);
+
+       idx = get_seg_reg_override_idx(insn);
+       if (idx < 0)
+               return idx;
+
+       if (idx == INAT_SEG_REG_DEFAULT)
+               return resolve_default_seg(insn, regs, regoff);
+
+       /*
+        * In long mode, segment override prefixes are ignored, except for
+        * overrides for FS and GS.
+        */
+       if (user_64bit_mode(regs)) {
+               if (idx != INAT_SEG_REG_FS &&
+                   idx != INAT_SEG_REG_GS)
+                       idx = INAT_SEG_REG_IGNORE;
+       }
+
+       return idx;
+}
+
+/**
+ * get_segment_selector() - obtain segment selector
+ * @regs:              Register values as seen when entering kernel mode
+ * @seg_reg_idx:       Segment register index to use
+ *
+ * Obtain the segment selector from any of the CS, SS, DS, ES, FS, GS segment
+ * registers. In CONFIG_X86_32, the segment is obtained from either pt_regs or
+ * kernel_vm86_regs as applicable. In CONFIG_X86_64, CS and SS are obtained
+ * from pt_regs. DS, ES, FS and GS are obtained by reading the actual CPU
+ * registers. This done for only for completeness as in CONFIG_X86_64 segment
+ * registers are ignored.
+ *
+ * Returns:
+ *
+ * Value of the segment selector, including null when running in
+ * long mode.
+ *
+ * -EINVAL on error.
+ */
+static short get_segment_selector(struct pt_regs *regs, int seg_reg_idx)
+{
+#ifdef CONFIG_X86_64
+       unsigned short sel;
+
+       switch (seg_reg_idx) {
+       case INAT_SEG_REG_IGNORE:
+               return 0;
+       case INAT_SEG_REG_CS:
+               return (unsigned short)(regs->cs & 0xffff);
+       case INAT_SEG_REG_SS:
+               return (unsigned short)(regs->ss & 0xffff);
+       case INAT_SEG_REG_DS:
+               savesegment(ds, sel);
+               return sel;
+       case INAT_SEG_REG_ES:
+               savesegment(es, sel);
+               return sel;
+       case INAT_SEG_REG_FS:
+               savesegment(fs, sel);
+               return sel;
+       case INAT_SEG_REG_GS:
+               savesegment(gs, sel);
+               return sel;
+       default:
+               return -EINVAL;
+       }
+#else /* CONFIG_X86_32 */
+       struct kernel_vm86_regs *vm86regs = (struct kernel_vm86_regs *)regs;
+
+       if (v8086_mode(regs)) {
+               switch (seg_reg_idx) {
+               case INAT_SEG_REG_CS:
+                       return (unsigned short)(regs->cs & 0xffff);
+               case INAT_SEG_REG_SS:
+                       return (unsigned short)(regs->ss & 0xffff);
+               case INAT_SEG_REG_DS:
+                       return vm86regs->ds;
+               case INAT_SEG_REG_ES:
+                       return vm86regs->es;
+               case INAT_SEG_REG_FS:
+                       return vm86regs->fs;
+               case INAT_SEG_REG_GS:
+                       return vm86regs->gs;
+               case INAT_SEG_REG_IGNORE:
+                       /* fall through */
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       switch (seg_reg_idx) {
+       case INAT_SEG_REG_CS:
+               return (unsigned short)(regs->cs & 0xffff);
+       case INAT_SEG_REG_SS:
+               return (unsigned short)(regs->ss & 0xffff);
+       case INAT_SEG_REG_DS:
+               return (unsigned short)(regs->ds & 0xffff);
+       case INAT_SEG_REG_ES:
+               return (unsigned short)(regs->es & 0xffff);
+       case INAT_SEG_REG_FS:
+               return (unsigned short)(regs->fs & 0xffff);
+       case INAT_SEG_REG_GS:
+               /*
+                * GS may or may not be in regs as per CONFIG_X86_32_LAZY_GS.
+                * The macro below takes care of both cases.
+                */
+               return get_user_gs(regs);
+       case INAT_SEG_REG_IGNORE:
+               /* fall through */
+       default:
+               return -EINVAL;
+       }
+#endif /* CONFIG_X86_64 */
+}
+
 static int get_reg_offset(struct insn *insn, struct pt_regs *regs,
                          enum reg_type type)
 {