diff options
author | Will Deacon <will.deacon@arm.com> | 2010-06-25 07:24:53 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-07-09 09:41:34 -0400 |
commit | e513f8bf240d34bd6e732ba2f74df9ab84686ce6 (patch) | |
tree | 989f646d90b42b583ea1cd51d34ecccf9afd8027 | |
parent | eb668c6d06dd4f935fc610207c58a5f221384651 (diff) |
ARM: 6199/1: Add kprobe-based event tracer
This patch enables the HAVE_REGS_AND_STACK_ACCESS_API option
for ARM which is required by the kprobe events tracer. Code based
on the PowerPC port.
Cc: Jean Pihet <jpihet@mvista.com>
Tested-by: Jamie Iles <jamie.iles@picochip.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/include/asm/ptrace.h | 36 | ||||
-rw-r--r-- | arch/arm/kernel/ptrace.c | 96 |
3 files changed, 133 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9a0ed364d4f7..aa738aa70c78 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -24,6 +24,7 @@ config ARM | |||
24 | select HAVE_KERNEL_LZMA | 24 | select HAVE_KERNEL_LZMA |
25 | select HAVE_PERF_EVENTS | 25 | select HAVE_PERF_EVENTS |
26 | select PERF_USE_VMALLOC | 26 | select PERF_USE_VMALLOC |
27 | select HAVE_REGS_AND_STACK_ACCESS_API | ||
27 | help | 28 | help |
28 | The ARM series is a line of low-power-consumption RISC chip designs | 29 | The ARM series is a line of low-power-consumption RISC chip designs |
29 | licensed by ARM Ltd and targeted at embedded applications and | 30 | licensed by ARM Ltd and targeted at embedded applications and |
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index 9dcb11e59026..c974be8913a7 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h | |||
@@ -184,6 +184,42 @@ extern unsigned long profile_pc(struct pt_regs *regs); | |||
184 | #define predicate(x) ((x) & 0xf0000000) | 184 | #define predicate(x) ((x) & 0xf0000000) |
185 | #define PREDICATE_ALWAYS 0xe0000000 | 185 | #define PREDICATE_ALWAYS 0xe0000000 |
186 | 186 | ||
187 | /* | ||
188 | * kprobe-based event tracer support | ||
189 | */ | ||
190 | #include <linux/stddef.h> | ||
191 | #include <linux/types.h> | ||
192 | #define MAX_REG_OFFSET (offsetof(struct pt_regs, ARM_ORIG_r0)) | ||
193 | |||
194 | extern int regs_query_register_offset(const char *name); | ||
195 | extern const char *regs_query_register_name(unsigned int offset); | ||
196 | extern bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr); | ||
197 | extern unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, | ||
198 | unsigned int n); | ||
199 | |||
200 | /** | ||
201 | * regs_get_register() - get register value from its offset | ||
202 | * @regs: pt_regs from which register value is gotten | ||
203 | * @offset: offset number of the register. | ||
204 | * | ||
205 | * regs_get_register returns the value of a register whose offset from @regs. | ||
206 | * The @offset is the offset of the register in struct pt_regs. | ||
207 | * If @offset is bigger than MAX_REG_OFFSET, this returns 0. | ||
208 | */ | ||
209 | static inline unsigned long regs_get_register(struct pt_regs *regs, | ||
210 | unsigned int offset) | ||
211 | { | ||
212 | if (unlikely(offset > MAX_REG_OFFSET)) | ||
213 | return 0; | ||
214 | return *(unsigned long *)((unsigned long)regs + offset); | ||
215 | } | ||
216 | |||
217 | /* Valid only for Kernel mode traps. */ | ||
218 | static inline unsigned long kernel_stack_pointer(struct pt_regs *regs) | ||
219 | { | ||
220 | return regs->ARM_sp; | ||
221 | } | ||
222 | |||
187 | #endif /* __KERNEL__ */ | 223 | #endif /* __KERNEL__ */ |
188 | 224 | ||
189 | #endif /* __ASSEMBLY__ */ | 225 | #endif /* __ASSEMBLY__ */ |
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 3f562a7c0a99..f99d489822d5 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c | |||
@@ -52,6 +52,102 @@ | |||
52 | #define BREAKINST_THUMB 0xde01 | 52 | #define BREAKINST_THUMB 0xde01 |
53 | #endif | 53 | #endif |
54 | 54 | ||
55 | struct pt_regs_offset { | ||
56 | const char *name; | ||
57 | int offset; | ||
58 | }; | ||
59 | |||
60 | #define REG_OFFSET_NAME(r) \ | ||
61 | {.name = #r, .offset = offsetof(struct pt_regs, ARM_##r)} | ||
62 | #define REG_OFFSET_END {.name = NULL, .offset = 0} | ||
63 | |||
64 | static const struct pt_regs_offset regoffset_table[] = { | ||
65 | REG_OFFSET_NAME(r0), | ||
66 | REG_OFFSET_NAME(r1), | ||
67 | REG_OFFSET_NAME(r2), | ||
68 | REG_OFFSET_NAME(r3), | ||
69 | REG_OFFSET_NAME(r4), | ||
70 | REG_OFFSET_NAME(r5), | ||
71 | REG_OFFSET_NAME(r6), | ||
72 | REG_OFFSET_NAME(r7), | ||
73 | REG_OFFSET_NAME(r8), | ||
74 | REG_OFFSET_NAME(r9), | ||
75 | REG_OFFSET_NAME(r10), | ||
76 | REG_OFFSET_NAME(fp), | ||
77 | REG_OFFSET_NAME(ip), | ||
78 | REG_OFFSET_NAME(sp), | ||
79 | REG_OFFSET_NAME(lr), | ||
80 | REG_OFFSET_NAME(pc), | ||
81 | REG_OFFSET_NAME(cpsr), | ||
82 | REG_OFFSET_NAME(ORIG_r0), | ||
83 | REG_OFFSET_END, | ||
84 | }; | ||
85 | |||
86 | /** | ||
87 | * regs_query_register_offset() - query register offset from its name | ||
88 | * @name: the name of a register | ||
89 | * | ||
90 | * regs_query_register_offset() returns the offset of a register in struct | ||
91 | * pt_regs from its name. If the name is invalid, this returns -EINVAL; | ||
92 | */ | ||
93 | int regs_query_register_offset(const char *name) | ||
94 | { | ||
95 | const struct pt_regs_offset *roff; | ||
96 | for (roff = regoffset_table; roff->name != NULL; roff++) | ||
97 | if (!strcmp(roff->name, name)) | ||
98 | return roff->offset; | ||
99 | return -EINVAL; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * regs_query_register_name() - query register name from its offset | ||
104 | * @offset: the offset of a register in struct pt_regs. | ||
105 | * | ||
106 | * regs_query_register_name() returns the name of a register from its | ||
107 | * offset in struct pt_regs. If the @offset is invalid, this returns NULL; | ||
108 | */ | ||
109 | const char *regs_query_register_name(unsigned int offset) | ||
110 | { | ||
111 | const struct pt_regs_offset *roff; | ||
112 | for (roff = regoffset_table; roff->name != NULL; roff++) | ||
113 | if (roff->offset == offset) | ||
114 | return roff->name; | ||
115 | return NULL; | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * regs_within_kernel_stack() - check the address in the stack | ||
120 | * @regs: pt_regs which contains kernel stack pointer. | ||
121 | * @addr: address which is checked. | ||
122 | * | ||
123 | * regs_within_kernel_stack() checks @addr is within the kernel stack page(s). | ||
124 | * If @addr is within the kernel stack, it returns true. If not, returns false. | ||
125 | */ | ||
126 | bool regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr) | ||
127 | { | ||
128 | return ((addr & ~(THREAD_SIZE - 1)) == | ||
129 | (kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1))); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * regs_get_kernel_stack_nth() - get Nth entry of the stack | ||
134 | * @regs: pt_regs which contains kernel stack pointer. | ||
135 | * @n: stack entry number. | ||
136 | * | ||
137 | * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which | ||
138 | * is specified by @regs. If the @n th entry is NOT in the kernel stack, | ||
139 | * this returns 0. | ||
140 | */ | ||
141 | unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n) | ||
142 | { | ||
143 | unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs); | ||
144 | addr += n; | ||
145 | if (regs_within_kernel_stack(regs, (unsigned long)addr)) | ||
146 | return *addr; | ||
147 | else | ||
148 | return 0; | ||
149 | } | ||
150 | |||
55 | /* | 151 | /* |
56 | * this routine will get a word off of the processes privileged stack. | 152 | * this routine will get a word off of the processes privileged stack. |
57 | * the offset is how far from the base addr as stored in the THREAD. | 153 | * the offset is how far from the base addr as stored in the THREAD. |