]> rtime.felk.cvut.cz Git - can-eth-gw-linux.git/blobdiff - arch/x86/kernel/ptrace.c
Merge branch 'rcu/next' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck...
[can-eth-gw-linux.git] / arch / x86 / kernel / ptrace.c
index 65b88a5dc1a8a2f5d4cb5ff76b60b1756f8f050d..b629bbe0d9bdeee5a416a8a6390167e595cf6036 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/signal.h>
 #include <linux/perf_event.h>
 #include <linux/hw_breakpoint.h>
+#include <linux/rcupdate.h>
+#include <linux/module.h>
 #include <linux/context_tracking.h>
 
 #include <asm/uaccess.h>
@@ -166,6 +168,35 @@ static inline bool invalid_selector(u16 value)
 
 #define FLAG_MASK              FLAG_MASK_32
 
+/*
+ * X86_32 CPUs don't save ss and esp if the CPU is already in kernel mode
+ * when it traps.  The previous stack will be directly underneath the saved
+ * registers, and 'sp/ss' won't even have been saved. Thus the '&regs->sp'.
+ *
+ * Now, if the stack is empty, '&regs->sp' is out of range. In this
+ * case we try to take the previous stack. To always return a non-null
+ * stack pointer we fall back to regs as stack if no previous stack
+ * exists.
+ *
+ * This is valid only for kernel mode traps.
+ */
+unsigned long kernel_stack_pointer(struct pt_regs *regs)
+{
+       unsigned long context = (unsigned long)regs & ~(THREAD_SIZE - 1);
+       unsigned long sp = (unsigned long)&regs->sp;
+       struct thread_info *tinfo;
+
+       if (context == (sp & ~(THREAD_SIZE - 1)))
+               return sp;
+
+       tinfo = (struct thread_info *)context;
+       if (tinfo->previous_esp)
+               return tinfo->previous_esp;
+
+       return (unsigned long)regs;
+}
+EXPORT_SYMBOL_GPL(kernel_stack_pointer);
+
 static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long regno)
 {
        BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0);