[PATCH v4 07/17] x86/insn-eval: Add utility function to get segment descriptor base address
From: Ricardo Neri
Date: Thu Feb 23 2017 - 01:42:09 EST
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 <dave.hansen@xxxxxxxxxxxxxxx>
Cc: Adam Buchbinder <adam.buchbinder@xxxxxxxxx>
Cc: Colin Ian King <colin.king@xxxxxxxxxxxxx>
Cc: Lorenzo Stoakes <lstoakes@xxxxxxxxx>
Cc: Qiaowei Ren <qiaowei.ren@xxxxxxxxx>
Cc: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Cc: Masami Hiramatsu <mhiramat@xxxxxxxxxx>
Cc: Adrian Hunter <adrian.hunter@xxxxxxxxx>
Cc: Kees Cook <keescook@xxxxxxxxxxxx>
Cc: Thomas Garnier <thgarnie@xxxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Borislav Petkov <bp@xxxxxxx>
Cc: Dmitry Vyukov <dvyukov@xxxxxxxxxx>
Cc: Ravi V. Shankar <ravi.v.shankar@xxxxxxxxx>
Cc: x86@xxxxxxxxxx
Signed-off-by: Ricardo Neri <ricardo.neri-calderon@xxxxxxxxxxxxxxx>
---
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