diff options
-rw-r--r-- | arch/i386/kernel/kprobes.c | 19 | ||||
-rw-r--r-- | arch/ppc64/kernel/kprobes.c | 19 | ||||
-rw-r--r-- | arch/sparc64/kernel/kprobes.c | 31 | ||||
-rw-r--r-- | arch/x86_64/kernel/kprobes.c | 26 | ||||
-rw-r--r-- | include/linux/kprobes.h | 2 | ||||
-rw-r--r-- | kernel/kprobes.c | 12 |
6 files changed, 72 insertions, 37 deletions
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index 048f754bbe23..2314d8d306fd 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/ptrace.h> | 33 | #include <linux/ptrace.h> |
34 | #include <linux/spinlock.h> | 34 | #include <linux/spinlock.h> |
35 | #include <linux/preempt.h> | 35 | #include <linux/preempt.h> |
36 | #include <asm/cacheflush.h> | ||
36 | #include <asm/kdebug.h> | 37 | #include <asm/kdebug.h> |
37 | #include <asm/desc.h> | 38 | #include <asm/desc.h> |
38 | 39 | ||
@@ -71,16 +72,25 @@ int arch_prepare_kprobe(struct kprobe *p) | |||
71 | void arch_copy_kprobe(struct kprobe *p) | 72 | void arch_copy_kprobe(struct kprobe *p) |
72 | { | 73 | { |
73 | memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); | 74 | memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); |
75 | p->opcode = *p->addr; | ||
74 | } | 76 | } |
75 | 77 | ||
76 | void arch_remove_kprobe(struct kprobe *p) | 78 | void arch_arm_kprobe(struct kprobe *p) |
77 | { | 79 | { |
80 | *p->addr = BREAKPOINT_INSTRUCTION; | ||
81 | flush_icache_range((unsigned long) p->addr, | ||
82 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
78 | } | 83 | } |
79 | 84 | ||
80 | static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) | 85 | void arch_disarm_kprobe(struct kprobe *p) |
81 | { | 86 | { |
82 | *p->addr = p->opcode; | 87 | *p->addr = p->opcode; |
83 | regs->eip = (unsigned long)p->addr; | 88 | flush_icache_range((unsigned long) p->addr, |
89 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
90 | } | ||
91 | |||
92 | void arch_remove_kprobe(struct kprobe *p) | ||
93 | { | ||
84 | } | 94 | } |
85 | 95 | ||
86 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 96 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) |
@@ -177,7 +187,8 @@ static int kprobe_handler(struct pt_regs *regs) | |||
177 | unlock_kprobes(); | 187 | unlock_kprobes(); |
178 | goto no_kprobe; | 188 | goto no_kprobe; |
179 | } | 189 | } |
180 | disarm_kprobe(p, regs); | 190 | arch_disarm_kprobe(p); |
191 | regs->eip = (unsigned long)p->addr; | ||
181 | ret = 1; | 192 | ret = 1; |
182 | } else { | 193 | } else { |
183 | p = current_kprobe; | 194 | p = current_kprobe; |
diff --git a/arch/ppc64/kernel/kprobes.c b/arch/ppc64/kernel/kprobes.c index e950a2058a19..8c0920a6d03e 100644 --- a/arch/ppc64/kernel/kprobes.c +++ b/arch/ppc64/kernel/kprobes.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/ptrace.h> | 32 | #include <linux/ptrace.h> |
33 | #include <linux/spinlock.h> | 33 | #include <linux/spinlock.h> |
34 | #include <linux/preempt.h> | 34 | #include <linux/preempt.h> |
35 | #include <asm/cacheflush.h> | ||
35 | #include <asm/kdebug.h> | 36 | #include <asm/kdebug.h> |
36 | #include <asm/sstep.h> | 37 | #include <asm/sstep.h> |
37 | 38 | ||
@@ -61,16 +62,25 @@ int arch_prepare_kprobe(struct kprobe *p) | |||
61 | void arch_copy_kprobe(struct kprobe *p) | 62 | void arch_copy_kprobe(struct kprobe *p) |
62 | { | 63 | { |
63 | memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); | 64 | memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); |
65 | p->opcode = *p->addr; | ||
64 | } | 66 | } |
65 | 67 | ||
66 | void arch_remove_kprobe(struct kprobe *p) | 68 | void arch_arm_kprobe(struct kprobe *p) |
67 | { | 69 | { |
70 | *p->addr = BREAKPOINT_INSTRUCTION; | ||
71 | flush_icache_range((unsigned long) p->addr, | ||
72 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
68 | } | 73 | } |
69 | 74 | ||
70 | static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) | 75 | void arch_disarm_kprobe(struct kprobe *p) |
71 | { | 76 | { |
72 | *p->addr = p->opcode; | 77 | *p->addr = p->opcode; |
73 | regs->nip = (unsigned long)p->addr; | 78 | flush_icache_range((unsigned long) p->addr, |
79 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
80 | } | ||
81 | |||
82 | void arch_remove_kprobe(struct kprobe *p) | ||
83 | { | ||
74 | } | 84 | } |
75 | 85 | ||
76 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 86 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) |
@@ -101,7 +111,8 @@ static inline int kprobe_handler(struct pt_regs *regs) | |||
101 | unlock_kprobes(); | 111 | unlock_kprobes(); |
102 | goto no_kprobe; | 112 | goto no_kprobe; |
103 | } | 113 | } |
104 | disarm_kprobe(p, regs); | 114 | arch_disarm_kprobe(p); |
115 | regs->nip = (unsigned long)p->addr; | ||
105 | ret = 1; | 116 | ret = 1; |
106 | } else { | 117 | } else { |
107 | p = current_kprobe; | 118 | p = current_kprobe; |
diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c index 7066d7ba667a..d67195ba3fa2 100644 --- a/arch/sparc64/kernel/kprobes.c +++ b/arch/sparc64/kernel/kprobes.c | |||
@@ -6,7 +6,6 @@ | |||
6 | #include <linux/config.h> | 6 | #include <linux/config.h> |
7 | #include <linux/kernel.h> | 7 | #include <linux/kernel.h> |
8 | #include <linux/kprobes.h> | 8 | #include <linux/kprobes.h> |
9 | |||
10 | #include <asm/kdebug.h> | 9 | #include <asm/kdebug.h> |
11 | #include <asm/signal.h> | 10 | #include <asm/signal.h> |
12 | 11 | ||
@@ -47,6 +46,19 @@ void arch_copy_kprobe(struct kprobe *p) | |||
47 | { | 46 | { |
48 | p->ainsn.insn[0] = *p->addr; | 47 | p->ainsn.insn[0] = *p->addr; |
49 | p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2; | 48 | p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2; |
49 | p->opcode = *p->addr; | ||
50 | } | ||
51 | |||
52 | void arch_arm_kprobe(struct kprobe *p) | ||
53 | { | ||
54 | *p->addr = BREAKPOINT_INSTRUCTION; | ||
55 | flushi(p->addr); | ||
56 | } | ||
57 | |||
58 | void arch_disarm_kprobe(struct kprobe *p) | ||
59 | { | ||
60 | *p->addr = p->opcode; | ||
61 | flushi(p->addr); | ||
50 | } | 62 | } |
51 | 63 | ||
52 | void arch_remove_kprobe(struct kprobe *p) | 64 | void arch_remove_kprobe(struct kprobe *p) |
@@ -78,17 +90,6 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | |||
78 | } | 90 | } |
79 | } | 91 | } |
80 | 92 | ||
81 | static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) | ||
82 | { | ||
83 | *p->addr = p->opcode; | ||
84 | flushi(p->addr); | ||
85 | |||
86 | regs->tpc = (unsigned long) p->addr; | ||
87 | regs->tnpc = current_kprobe_orig_tnpc; | ||
88 | regs->tstate = ((regs->tstate & ~TSTATE_PIL) | | ||
89 | current_kprobe_orig_tstate_pil); | ||
90 | } | ||
91 | |||
92 | static int kprobe_handler(struct pt_regs *regs) | 93 | static int kprobe_handler(struct pt_regs *regs) |
93 | { | 94 | { |
94 | struct kprobe *p; | 95 | struct kprobe *p; |
@@ -109,7 +110,11 @@ static int kprobe_handler(struct pt_regs *regs) | |||
109 | unlock_kprobes(); | 110 | unlock_kprobes(); |
110 | goto no_kprobe; | 111 | goto no_kprobe; |
111 | } | 112 | } |
112 | disarm_kprobe(p, regs); | 113 | arch_disarm_kprobe(p); |
114 | regs->tpc = (unsigned long) p->addr; | ||
115 | regs->tnpc = current_kprobe_orig_tnpc; | ||
116 | regs->tstate = ((regs->tstate & ~TSTATE_PIL) | | ||
117 | current_kprobe_orig_tstate_pil); | ||
113 | ret = 1; | 118 | ret = 1; |
114 | } else { | 119 | } else { |
115 | p = current_kprobe; | 120 | p = current_kprobe; |
diff --git a/arch/x86_64/kernel/kprobes.c b/arch/x86_64/kernel/kprobes.c index 203672ca7401..324bf57925a9 100644 --- a/arch/x86_64/kernel/kprobes.c +++ b/arch/x86_64/kernel/kprobes.c | |||
@@ -39,7 +39,7 @@ | |||
39 | #include <linux/slab.h> | 39 | #include <linux/slab.h> |
40 | #include <linux/preempt.h> | 40 | #include <linux/preempt.h> |
41 | #include <linux/moduleloader.h> | 41 | #include <linux/moduleloader.h> |
42 | 42 | #include <asm/cacheflush.h> | |
43 | #include <asm/pgtable.h> | 43 | #include <asm/pgtable.h> |
44 | #include <asm/kdebug.h> | 44 | #include <asm/kdebug.h> |
45 | 45 | ||
@@ -216,19 +216,28 @@ void arch_copy_kprobe(struct kprobe *p) | |||
216 | BUG_ON((s64) (s32) disp != disp); /* Sanity check. */ | 216 | BUG_ON((s64) (s32) disp != disp); /* Sanity check. */ |
217 | *ripdisp = disp; | 217 | *ripdisp = disp; |
218 | } | 218 | } |
219 | p->opcode = *p->addr; | ||
219 | } | 220 | } |
220 | 221 | ||
221 | void arch_remove_kprobe(struct kprobe *p) | 222 | void arch_arm_kprobe(struct kprobe *p) |
222 | { | 223 | { |
223 | up(&kprobe_mutex); | 224 | *p->addr = BREAKPOINT_INSTRUCTION; |
224 | free_insn_slot(p->ainsn.insn); | 225 | flush_icache_range((unsigned long) p->addr, |
225 | down(&kprobe_mutex); | 226 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); |
226 | } | 227 | } |
227 | 228 | ||
228 | static inline void disarm_kprobe(struct kprobe *p, struct pt_regs *regs) | 229 | void arch_disarm_kprobe(struct kprobe *p) |
229 | { | 230 | { |
230 | *p->addr = p->opcode; | 231 | *p->addr = p->opcode; |
231 | regs->rip = (unsigned long)p->addr; | 232 | flush_icache_range((unsigned long) p->addr, |
233 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
234 | } | ||
235 | |||
236 | void arch_remove_kprobe(struct kprobe *p) | ||
237 | { | ||
238 | up(&kprobe_mutex); | ||
239 | free_insn_slot(p->ainsn.insn); | ||
240 | down(&kprobe_mutex); | ||
232 | } | 241 | } |
233 | 242 | ||
234 | static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 243 | static void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) |
@@ -311,7 +320,8 @@ int kprobe_handler(struct pt_regs *regs) | |||
311 | unlock_kprobes(); | 320 | unlock_kprobes(); |
312 | goto no_kprobe; | 321 | goto no_kprobe; |
313 | } | 322 | } |
314 | disarm_kprobe(p, regs); | 323 | arch_disarm_kprobe(p); |
324 | regs->rip = (unsigned long)p->addr; | ||
315 | ret = 1; | 325 | ret = 1; |
316 | } else { | 326 | } else { |
317 | p = current_kprobe; | 327 | p = current_kprobe; |
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index fba39f87efec..0f90466fb8b0 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h | |||
@@ -165,6 +165,8 @@ static inline int kprobe_running(void) | |||
165 | 165 | ||
166 | extern int arch_prepare_kprobe(struct kprobe *p); | 166 | extern int arch_prepare_kprobe(struct kprobe *p); |
167 | extern void arch_copy_kprobe(struct kprobe *p); | 167 | extern void arch_copy_kprobe(struct kprobe *p); |
168 | extern void arch_arm_kprobe(struct kprobe *p); | ||
169 | extern void arch_disarm_kprobe(struct kprobe *p); | ||
168 | extern void arch_remove_kprobe(struct kprobe *p); | 170 | extern void arch_remove_kprobe(struct kprobe *p); |
169 | extern void show_registers(struct pt_regs *regs); | 171 | extern void show_registers(struct pt_regs *regs); |
170 | 172 | ||
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 692fbf75ab49..e8e0ae8a6e14 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -261,7 +261,7 @@ static inline void free_rp_inst(struct kretprobe *rp) | |||
261 | static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p) | 261 | static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p) |
262 | { | 262 | { |
263 | ap->addr = p->addr; | 263 | ap->addr = p->addr; |
264 | ap->opcode = p->opcode; | 264 | memcpy(&ap->opcode, &p->opcode, sizeof(kprobe_opcode_t)); |
265 | memcpy(&ap->ainsn, &p->ainsn, sizeof(struct arch_specific_insn)); | 265 | memcpy(&ap->ainsn, &p->ainsn, sizeof(struct arch_specific_insn)); |
266 | 266 | ||
267 | ap->pre_handler = aggr_pre_handler; | 267 | ap->pre_handler = aggr_pre_handler; |
@@ -304,10 +304,8 @@ static int register_aggr_kprobe(struct kprobe *old_p, struct kprobe *p) | |||
304 | /* kprobe removal house-keeping routines */ | 304 | /* kprobe removal house-keeping routines */ |
305 | static inline void cleanup_kprobe(struct kprobe *p, unsigned long flags) | 305 | static inline void cleanup_kprobe(struct kprobe *p, unsigned long flags) |
306 | { | 306 | { |
307 | *p->addr = p->opcode; | 307 | arch_disarm_kprobe(p); |
308 | hlist_del(&p->hlist); | 308 | hlist_del(&p->hlist); |
309 | flush_icache_range((unsigned long) p->addr, | ||
310 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
311 | spin_unlock_irqrestore(&kprobe_lock, flags); | 309 | spin_unlock_irqrestore(&kprobe_lock, flags); |
312 | arch_remove_kprobe(p); | 310 | arch_remove_kprobe(p); |
313 | } | 311 | } |
@@ -344,10 +342,8 @@ int register_kprobe(struct kprobe *p) | |||
344 | hlist_add_head(&p->hlist, | 342 | hlist_add_head(&p->hlist, |
345 | &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]); | 343 | &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]); |
346 | 344 | ||
347 | p->opcode = *p->addr; | 345 | arch_arm_kprobe(p); |
348 | *p->addr = BREAKPOINT_INSTRUCTION; | 346 | |
349 | flush_icache_range((unsigned long) p->addr, | ||
350 | (unsigned long) p->addr + sizeof(kprobe_opcode_t)); | ||
351 | out: | 347 | out: |
352 | spin_unlock_irqrestore(&kprobe_lock, flags); | 348 | spin_unlock_irqrestore(&kprobe_lock, flags); |
353 | rm_kprobe: | 349 | rm_kprobe: |