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. |
