When computing a linear address and segmentation is used, we need to know
the base address of the segment involved in the computation. In most of
the cases, it will be sufficient to use USER_DS, which has a base of 0.
However, it may be possible that a user space program defines its own
segments via a local descriptor table. Thus, the base address of the
segment is needed.

The segment selector to be used when computing a linear address is
determined by any either segment select override prefixes in the
instruction or the registers involved in the computation of the effective
address; in that order. Also, there are cases when the overrides shall be
ignored.

This function can be used in both protected mode and virtual-8086 mode.
When in protected mode, the segment selector is obtained from the pt_regs
structure. When in virtual-8086 mode, data segments are obtained from the
kernel_vm86_regs.

When in CONFIG_X86_64, selectors for data segments are absent from pt_regs.
Hence, the returned selector is zero to signal that segmentation is not
in use.

Cc: Dave Hansen <dave.han...@linux.intel.com>
Cc: Adam Buchbinder <adam.buchbin...@gmail.com>
Cc: Colin Ian King <colin.k...@canonical.com>
Cc: Lorenzo Stoakes <lstoa...@gmail.com>
Cc: Qiaowei Ren <qiaowei....@intel.com>
Cc: Arnaldo Carvalho de Melo <a...@redhat.com>
Cc: Masami Hiramatsu <mhira...@kernel.org>
Cc: Adrian Hunter <adrian.hun...@intel.com>
Cc: Kees Cook <keesc...@chromium.org>
Cc: Thomas Garnier <thgar...@google.com>
Cc: Peter Zijlstra <pet...@infradead.org>
Cc: Borislav Petkov <b...@suse.de>
Cc: Dmitry Vyukov <dvyu...@google.com>
Cc: Ravi V. Shankar <ravi.v.shan...@intel.com>
Cc: x...@kernel.org
Signed-off-by: Ricardo Neri <ricardo.neri-calde...@linux.intel.com>
---
 arch/x86/lib/insn-eval.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 163 insertions(+)

diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index 6c62fbf..516902e 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -8,6 +8,7 @@
 #include <asm/inat.h>
 #include <asm/insn.h>
 #include <asm/insn-eval.h>
+#include <asm/vm86.h>
 
 enum reg_type {
        REG_TYPE_RM = 0,
@@ -15,6 +16,168 @@ enum reg_type {
        REG_TYPE_BASE,
 };
 
+/**
+ * get_segment_selector() - obtain segment selector
+ * @regs:      Set of registers containing the segment selector
+ * @insn:      Instruction structure with selector override prefixes
+ * @regoff:    Operand offset, in pt_regs, of which the selector is needed
+ *
+ * The segment selector to which an effective address refers depends on
+ * a) segment selector overrides instruction prefixes or b) the operand
+ * register indicated in the ModRM or SiB byte.
+ *
+ * For case a), the function inspects any prefixes in the insn instruction;
+ * insn can be null to indicate that selector override prefixes shall be
+ * ignored. This is useful when the use of prefixes is forbidden (e.g.,
+ * obtaining the code selector). For case b), the operand register shall be
+ * represented as the offset from the base address of pt_regs. Also, regoff
+ * can be -EINVAL for cases in which registers are not used as operands (e.g.,
+ * when the mod and r/m parts of the ModRM byte are 0 and 5, respectively).
+ *
+ * The returned segment selector is obtained from the regs structure. Both
+ * protected and virtual-8086 modes are supported. In virtual-8086 mode,
+ * data segments are obtained from the kernel_vm86_regs structure.
+ * For CONFIG_X86_64, the returned segment selector is null if such selector
+ * refers to es, fs or gs.
+ *
+ * Return: Value of the segment selector
+ */
+static unsigned short get_segment_selector(struct pt_regs *regs,
+                                          struct insn *insn, int regoff)
+{
+       int i;
+
+       struct kernel_vm86_regs *vm86regs = (struct kernel_vm86_regs *)regs;
+
+       if (!insn)
+               goto default_seg;
+
+       insn_get_prefixes(insn);
+
+       if (v8086_mode(regs)) {
+               /*
+                * Check first if we have selector overrides. Having more than
+                * one selector override leads to undefined behavior. We
+                * only use the first one and return
+                */
+               for (i = 0; i < insn->prefixes.nbytes; i++) {
+                       switch (insn->prefixes.bytes[i]) {
+                       /*
+                        * Code and stack segment selector register are saved in
+                        * all processor modes. Thus, it makes sense to take
+                        * them from pt_regs.
+                        */
+                       case 0x2e:
+                               return (unsigned short)regs->cs;
+                       case 0x36:
+                               return (unsigned short)regs->ss;
+                       /*
+                        * The rest of the segment selector registers are only
+                        * saved in virtual-8086 mode. Thus, we must obtain them
+                        * from the vm86 register structure.
+                        */
+                       case 0x3e:
+                               return vm86regs->ds;
+                       case 0x26:
+                               return vm86regs->es;
+                       case 0x64:
+                               return vm86regs->fs;
+                       case 0x65:
+                               return vm86regs->gs;
+                       /*
+                        * No default action needed. We simply did not find any
+                        * relevant prefixes.
+                        */
+                       }
+               }
+       } else {/* protected mode */
+               /*
+                * Check first if we have selector overrides. Having more than
+                * one selector override leads to undefined behavior. We
+                * only use the first one and return.
+                */
+               for (i = 0; i < insn->prefixes.nbytes; i++) {
+                       switch (insn->prefixes.bytes[i]) {
+                       /*
+                        * Code and stack segment selector register are saved in
+                        * all processor modes. Thus, it makes sense to take
+                        * them from pt_regs.
+                        */
+                       case 0x2e:
+                               return (unsigned short)regs->cs;
+                       case 0x36:
+                               return (unsigned short)regs->ss;
+#ifdef CONFIG_X86_32
+                       case 0x3e:
+                               return (unsigned short)regs->ds;
+                       case 0x26:
+                               return (unsigned short)regs->es;
+                       case 0x64:
+                               return (unsigned short)regs->fs;
+                       case 0x65:
+                               return (unsigned short)regs->gs;
+#else
+                       /* do not return any segment selector in x86_64 */
+                       case 0x3e:
+                       case 0x26:
+                       case 0x64:
+                       case 0x65:
+                               return 0;
+#endif
+                       /*
+                        * No default action needed. We simply did not find any
+                        * relevant prefixes.
+                        */
+                       }
+               }
+       }
+
+default_seg:
+       /*
+        * If no overrides, use default selectors as described in the
+        * Intel documentation: SS for ESP or EBP. DS for all data references,
+        * except when relative to stack or string destination.
+        * Also, AX, CX and DX are not valid register operands in 16-bit
+        * address encodings.
+        * Callers must interpret the result correctly according to the type
+        * of instructions (e.g., use ES for string instructions).
+        * Also, some values of modrm and sib might seem to indicate the use
+        * of EBP and ESP (e.g., modrm_mod = 0, modrm_rm = 5) but actually
+        * they refer to cases in which only a displacement used. These cases
+        * should be indentified by the caller and not with this function.
+        */
+       switch (regoff) {
+       case offsetof(struct pt_regs, ax):
+               /* fall through */
+       case offsetof(struct pt_regs, cx):
+               /* fall through */
+       case offsetof(struct pt_regs, dx):
+               if (insn && insn->addr_bytes == 2)
+                       return 0;
+       case -EDOM: /* no register involved in address computation */
+       case offsetof(struct pt_regs, bx):
+               /* fall through */
+       case offsetof(struct pt_regs, di):
+               /* fall through */
+       case offsetof(struct pt_regs, si):
+               if (v8086_mode(regs))
+                       return vm86regs->ds;
+#ifdef CONFIG_X86_32
+               return (unsigned short)regs->ds;
+#else
+               return 0;
+#endif
+       case offsetof(struct pt_regs, bp):
+               /* fall through */
+       case offsetof(struct pt_regs, sp):
+               return (unsigned short)regs->ss;
+       case offsetof(struct pt_regs, ip):
+               return (unsigned short)regs->cs;
+       default:
+               return 0;
+       }
+}
+
 static int get_reg_offset(struct insn *insn, struct pt_regs *regs,
                          enum reg_type type)
 {
-- 
2.9.3

--
To unsubscribe from this list: send the line "unsubscribe linux-msdos" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to