With segmentation, the base address of the segment descriptor is needed
to compute a linear address. The segment descriptor used in the address
computation depends on either any segment override prefixes in the in the
instruction or the default segment determined by the registers involved
in the address computation. Thus, both the instruction as well as the
register (specified as the offset from the base of pt_regs) are given as
inputs. Furthermore, if insn is null, overrides are ignored; this is
useful when, for instance, obtaining the base address of the instruction
pointer (the code segment is always used).

The segment selector is determined by get_seg_selector with the inputs
described above. Once the selector is known the base address is
determined. In protected mode, the selector is used to obtain the segment
descriptor and then its base address. In virtual-8086 mode, the base
address is computed as the value of the segment selector shifted 4
positions to the left.

Cc: Dave Hansen <[email protected]>
Cc: Adam Buchbinder <[email protected]>
Cc: Colin Ian King <[email protected]>
Cc: Lorenzo Stoakes <[email protected]>
Cc: Qiaowei Ren <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Thomas Garnier <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: Dmitry Vyukov <[email protected]>
Cc: Ravi V. Shankar <[email protected]>
Cc: [email protected]
Signed-off-by: Ricardo Neri <[email protected]>
---
 arch/x86/include/asm/insn-eval.h |  2 ++
 arch/x86/lib/insn-eval.c         | 42 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/arch/x86/include/asm/insn-eval.h b/arch/x86/include/asm/insn-eval.h
index 754211b..0de3083 100644
--- a/arch/x86/include/asm/insn-eval.h
+++ b/arch/x86/include/asm/insn-eval.h
@@ -15,5 +15,7 @@ void __user *insn_get_addr_ref(struct insn *insn, struct 
pt_regs *regs);
 int insn_get_reg_offset_modrm_rm(struct insn *insn, struct pt_regs *regs);
 int insn_get_reg_offset_sib_base(struct insn *insn, struct pt_regs *regs);
 int insn_get_reg_offset_sib_base(struct insn *insn, struct pt_regs *regs);
+unsigned long insn_get_seg_base(struct pt_regs *regs, struct insn *insn,
+                               int regoff);
 
 #endif /* _ASM_X86_INSN_EVAL_H */
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index e6d5dfb..4e3f797 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -323,6 +323,48 @@ static int get_desc(unsigned short seg, struct desc_struct 
**desc)
 }
 
 /**
+ * insn_get_seg_base() - Obtain base address contained in descriptor
+ * @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
+ *
+ * Obtain the base address of the segment descriptor as indicated by either any
+ * segment override prefixes contained in insn or the default segment 
applicable
+ * to the register indicated by regoff. regoff is specified as the offset in
+ * bytes from the base of pt_regs. If insn is not null and contain any segment
+ * override prefixes, the override is used instead of the default segment.
+ *
+ * Return: In protected mode, 0 if in CONFIG_X86_64, -1L in case of error,
+ * or the base address indicated in the selected segment descriptor. In
+ * virtual-8086, the segment selector shifted four positions to the right.
+ */
+unsigned long insn_get_seg_base(struct pt_regs *regs, struct insn *insn,
+                               int regoff)
+{
+       struct desc_struct *desc;
+       unsigned short seg;
+       int ret;
+
+       seg = get_segment_selector(regs, insn, regoff);
+
+       if (v8086_mode(regs))
+               /*
+                * Base is simply the segment selector sifted 4
+                * positions to the right.
+                */
+               return (unsigned long)(seg << 4);
+
+       /* 64-bit mode */
+       if (!seg)
+               return 0;
+       ret = get_desc(seg, &desc);
+       if (ret)
+               return -1L;
+
+       return get_desc_base(desc);
+}
+
+/**
  * insn_get_reg_offset_modrm_rm - Obtain register in r/m part of ModRM byte
  * @insn:      Instruction structure containing the ModRM byte
  * @regs:      Set of registers indicated by the ModRM byte
-- 
2.9.3

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

Reply via email to