The segment descriptor contains information that is relevant to how linear address need to be computed. It contains the default size of addresses as well as the base address of the segment. Thus, given a segment selector, we ought look at segment descriptor to correctly calculate the linear address.
In protected mode, the segment selector might indicate a segment descriptor from either the global descriptor table or a local descriptor table. Both cases are considered in this function. This function is the initial implementation for subsequent functions that will obtain the aforementioned attributes of the segment descriptor. 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/lib/insn-eval.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c index 0a496f4..f46cb31 100644 --- a/arch/x86/lib/insn-eval.c +++ b/arch/x86/lib/insn-eval.c @@ -6,9 +6,13 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/ratelimit.h> +#include <linux/mmu_context.h> +#include <asm/desc_defs.h> +#include <asm/desc.h> #include <asm/inat.h> #include <asm/insn.h> #include <asm/insn-eval.h> +#include <asm/ldt.h> #include <asm/vm86.h> enum reg_type { @@ -421,6 +425,57 @@ static int get_reg_offset(struct insn *insn, struct pt_regs *regs, } /** + * get_desc() - Obtain address of segment descriptor + * @sel: Segment selector + * + * Given a segment selector, obtain a pointer to the segment descriptor. + * Both global and local descriptor tables are supported. + * + * Return: pointer to segment descriptor on success. NULL on failure. + */ +static struct desc_struct *get_desc(unsigned short sel) +{ + struct desc_ptr gdt_desc = {0, 0}; + struct desc_struct *desc = NULL; + unsigned long desc_base; + +#ifdef CONFIG_MODIFY_LDT_SYSCALL + if ((sel & SEGMENT_TI_MASK) == SEGMENT_LDT) { + /* Bits [15:3] contain the index of the desired entry. */ + sel >>= 3; + + mutex_lock(¤t->active_mm->context.lock); + /* The size of the LDT refers to the number of entries. */ + if (!current->active_mm->context.ldt || + sel >= current->active_mm->context.ldt->size) { + mutex_unlock(¤t->active_mm->context.lock); + return NULL; + } + + desc = ¤t->active_mm->context.ldt->entries[sel]; + mutex_unlock(¤t->active_mm->context.lock); + return desc; + } +#endif + native_store_gdt(&gdt_desc); + + /* + * Segment descriptors have a size of 8 bytes. Thus, the index is + * multiplied by 8 to obtain the offset of the desired descriptor from + * the start of the GDT. As bits [15:3] of the segment selector contain + * the index, it can be regarded multiplied by 8 already. All that + * remains is to clear bits [2:0]. + */ + desc_base = sel & ~(SEGMENT_RPL_MASK | SEGMENT_TI_MASK); + + if (desc_base > gdt_desc.size) + return NULL; + + desc = (struct desc_struct *)(gdt_desc.address + desc_base); + return desc; +} + +/** * insn_get_reg_offset_modrm_rm() - Obtain register in r/m part of ModRM byte * @insn: Instruction structure containing the ModRM byte * @regs: Structure with register values as seen when entering kernel mode -- 2.9.3

