Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Will Deacon and committed by
Russell King
e513f8bf eb668c6d

+133
+1
arch/arm/Kconfig
··· 24 24 select HAVE_KERNEL_LZMA 25 25 select HAVE_PERF_EVENTS 26 26 select PERF_USE_VMALLOC 27 + select HAVE_REGS_AND_STACK_ACCESS_API 27 28 help 28 29 The ARM series is a line of low-power-consumption RISC chip designs 29 30 licensed by ARM Ltd and targeted at embedded applications and
+36
arch/arm/include/asm/ptrace.h
··· 184 184 #define predicate(x) ((x) & 0xf0000000) 185 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 223 #endif /* __KERNEL__ */ 188 224 189 225 #endif /* __ASSEMBLY__ */
+96
arch/arm/kernel/ptrace.c
··· 52 52 #define BREAKINST_THUMB 0xde01 53 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 152 * this routine will get a word off of the processes privileged stack. 57 153 * the offset is how far from the base addr as stored in the THREAD.