Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /** |
2 | * @file backtrace.c | |
3 | * | |
4 | * @remark Copyright 2002 OProfile authors | |
5 | * @remark Read the file COPYING | |
6 | * | |
7 | * @author John Levon | |
8 | * @author David Smith | |
9 | */ | |
10 | ||
11 | #include <linux/oprofile.h> | |
12 | #include <linux/sched.h> | |
13 | #include <linux/mm.h> | |
14 | #include <asm/ptrace.h> | |
c34d1b4d | 15 | #include <asm/uaccess.h> |
1da177e4 LT |
16 | |
17 | struct frame_head { | |
18 | struct frame_head * ebp; | |
19 | unsigned long ret; | |
20 | } __attribute__((packed)); | |
21 | ||
22 | static struct frame_head * | |
23 | dump_backtrace(struct frame_head * head) | |
24 | { | |
c34d1b4d | 25 | struct frame_head bufhead[2]; |
1da177e4 | 26 | |
c34d1b4d HD |
27 | /* Also check accessibility of one struct frame_head beyond */ |
28 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | |
29 | return NULL; | |
30 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | |
1da177e4 LT |
31 | return NULL; |
32 | ||
c34d1b4d | 33 | oprofile_add_trace(bufhead[0].ret); |
1da177e4 | 34 | |
c34d1b4d HD |
35 | /* frame pointers should strictly progress back up the stack |
36 | * (towards higher addresses) */ | |
37 | if (head >= bufhead[0].ebp) | |
38 | return NULL; | |
1da177e4 | 39 | |
c34d1b4d | 40 | return bufhead[0].ebp; |
1da177e4 LT |
41 | } |
42 | ||
43 | /* | |
44 | * | | /\ Higher addresses | |
45 | * | | | |
46 | * --------------- stack base (address of current_thread_info) | |
47 | * | thread info | | |
48 | * . . | |
49 | * | stack | | |
50 | * --------------- saved regs->ebp value if valid (frame_head address) | |
51 | * . . | |
23332c2e TL |
52 | * --------------- saved regs->rsp value if x86_64 |
53 | * | | | |
54 | * --------------- struct pt_regs * stored on stack if 32-bit | |
1da177e4 LT |
55 | * | | |
56 | * . . | |
57 | * | | | |
58 | * --------------- %esp | |
59 | * | | | |
60 | * | | \/ Lower addresses | |
61 | * | |
23332c2e TL |
62 | * Thus, regs (or regs->rsp for x86_64) <-> stack base restricts the |
63 | * valid(ish) ebp values. Note: (1) for x86_64, NMI and several other | |
64 | * exceptions use special stacks, maintained by the interrupt stack table | |
65 | * (IST). These stacks are set up in trap_init() in | |
66 | * arch/x86_64/kernel/traps.c. Thus, for x86_64, regs now does not point | |
67 | * to the kernel stack; instead, it points to some location on the NMI | |
68 | * stack. On the other hand, regs->rsp is the stack pointer saved when the | |
69 | * NMI occurred. (2) For 32-bit, regs->esp is not valid because the | |
70 | * processor does not save %esp on the kernel stack when interrupts occur | |
71 | * in the kernel mode. | |
1da177e4 LT |
72 | */ |
73 | #ifdef CONFIG_FRAME_POINTER | |
74 | static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs) | |
75 | { | |
76 | unsigned long headaddr = (unsigned long)head; | |
23332c2e TL |
77 | #ifdef CONFIG_X86_64 |
78 | unsigned long stack = (unsigned long)regs->rsp; | |
79 | #else | |
1da177e4 | 80 | unsigned long stack = (unsigned long)regs; |
23332c2e | 81 | #endif |
1da177e4 LT |
82 | unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE; |
83 | ||
84 | return headaddr > stack && headaddr < stack_base; | |
85 | } | |
86 | #else | |
87 | /* without fp, it's just junk */ | |
88 | static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs) | |
89 | { | |
90 | return 0; | |
91 | } | |
92 | #endif | |
93 | ||
94 | ||
95 | void | |
96 | x86_backtrace(struct pt_regs * const regs, unsigned int depth) | |
97 | { | |
98 | struct frame_head *head; | |
99 | ||
100 | #ifdef CONFIG_X86_64 | |
101 | head = (struct frame_head *)regs->rbp; | |
102 | #else | |
103 | head = (struct frame_head *)regs->ebp; | |
104 | #endif | |
105 | ||
fa1e1bdf | 106 | if (!user_mode_vm(regs)) { |
1da177e4 LT |
107 | while (depth-- && valid_kernel_stack(head, regs)) |
108 | head = dump_backtrace(head); | |
109 | return; | |
110 | } | |
111 | ||
c34d1b4d | 112 | while (depth-- && head) |
1da177e4 | 113 | head = dump_backtrace(head); |
1da177e4 | 114 | } |