x86: Move get_segment_eip() to step.c
PATCH 1/1 x86: Move get_segment_eip() to step.c
get_segment_eip has similarities to convert_rip_to_linear(), and is used in a similar context. Move get_segment_eip to step.c to allow easier consolidation.
---
Ingo, you may want to fold this into my previous patch:
x86: begin fault_{32|64}.c unification
it was a mistake to add the 32-bit version to X86_64 when it could be moved to step.c, I'll follow along with a bit of factoring of get_segment_eip and convert_rip_to_linear. This should allow elimination of many (all?) of the nasty #ifdefs in fault_32|64.c in is_prefetch().
arch/x86/kernel/step.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ arch/x86/mm/fault_32.c | 77 ------------------------------------------- arch/x86/mm/fault_64.c | 77 ------------------------------------------- include/asm-x86/ptrace.h | 2 + 4 files changed, 83 insertions(+), 154 deletions(-)
arch/x86/kernel/step.c +81 -0
@@ -6,6 +6,87 @@#include <linux/ptrace.h> #ifdef CONFIG_X86_32 +#include <linux/uaccess.h> + +#include <asm/desc.h> + +/* +* Return EIP plus the CS segment base. The segment limit is also +* adjusted, clamped to the kernel/user address space (whichever is +* appropriate), and returned in *eip_limit. +* +* The segment is checked, because it might have been changed by another +* task between the original faulting instruction and here. +* +* If CS is no longer a valid code segment, or if EIP is beyond the +* limit, or if it is a kernel address when CS is not a kernel segment, +* then the returned value will be greater than *eip_limit. +* +* This is slow, but is very rarely executed. +*/ +unsigned long get_segment_eip (struct pt_regs *regs, +unsigned long *eip_limit) +{ +unsigned long ip = regs->ip; +unsigned seg = regs->cs &0xffff ; +u32 seg_ar, seg_limit, base, *desc; + +/* Unlikely, but must come before segment checks. */ +if (unlikely (regs->flags &VM_MASK )) { + base = seg <<4 ; + *eip_limit = base +0xffff ; +return base + (ip &0xffff ); + } + +/* The standard kernel/user address space limit. */ + *eip_limit =user_mode (regs) ?USER_DS .seg :KERNEL_DS .seg; + +/* By far the most common cases. */ +if (likely (SEGMENT_IS_FLAT_CODE (seg))) +return ip; + +/* Check the segment exists, is within the current LDT/GDT size, +that kernel/user (ring 0..3) has the appropriate privilege, +that it's a code segment, and get the limit. */ + __asm__("larl %3,%0; lsll %3,%1" + :"=&r" (seg_ar),"=r" (seg_limit) :"0" (0 ),"rm" (seg)); +if ((~seg_ar &0x9800 ) || ip > seg_limit) { + *eip_limit =0 ; +return 1 ;/* So that returned ip > *eip_limit. */ + } + +/* Get the GDT/LDT descriptor base. +When you look for races in this code remember that +LDT and other horrors are only used in user space. */ +if (seg & (1 <<2 )) { +/* Must lock the LDT while reading it. */ +mutex_lock (¤t->mm->context.lock); + desc = current->mm->context.ldt; + desc = (void *)desc + (seg & ~7 ); + }else { +/* Must disable preemption while reading the GDT. */ + desc = (u32 *)get_cpu_gdt_table (get_cpu ()); + desc = (void *)desc + (seg & ~7 ); + } + +/* Decode the code segment base from the descriptor */ + base =get_desc_base ((struct desc_struct *)desc); + +if (seg & (1 <<2 )) +mutex_unlock (¤t->mm->context.lock); +else +put_cpu (); + +/* Adjust EIP and segment limit, and clamp at the kernel limit. +It's legitimate for segments to wrap at 0xffffffff. */ + seg_limit += base; +if (seg_limit < *eip_limit && seg_limit >= base) + *eip_limit = seg_limit; +return ip + base; +} +#endif + +#ifdef CONFIG_X86_32 static#endif unsigned long convert_rip_to_linear (struct task_struct *child,struct pt_regs *regs)
arch/x86/mm/fault_32.c +0 -77
83 lines not on this page · raw message
arch/x86/mm/fault_64.c +0 -77
83 lines not on this page · raw message
include/asm-x86/ptrace.h +2 -0
@@ -184,6 +184,8 @@ convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs);#ifdef __KERNEL__ +unsigned long get_segment_eip (struct pt_regs *regs,unsigned long *eip_limit); +/* * These are defined as per linux/ptrace.h, which see. */
Signature · 1 line
1.5.4.rc2.1164.g6451
Ingo Molnar
AppliedQuoted 3 lines · Harvey Harrison
get_segment_eiphas similarities toconvert_rip_to_linear(), and is used in a similar context. Moveget_segment_eiptostep.cto allow easier consolidation.
thanks, applied.
Quoted 3 lines
Ingo, you may want to fold this into my previous patch:
x86: begin fault_{32|64}.c unification
i'd rather keep this gradual. Unless there's a lot of flip-flop this is more maintainable. "use this new version for patch X" is harder (more error-prone) to do for a maintainer than "please merge this new gradual patch". We can fold too if really needed: due to bisection/build/crash error.
Ingo