aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/kprobes.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc64/kernel/kprobes.c')
-rw-r--r--arch/sparc64/kernel/kprobes.c83
1 files changed, 62 insertions, 21 deletions
diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c
index 7066d7ba667a..bdac631cf011 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,25 +46,59 @@ 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} 50}
51 51
52void arch_remove_kprobe(struct kprobe *p) 52void arch_arm_kprobe(struct kprobe *p)
53{ 53{
54 *p->addr = BREAKPOINT_INSTRUCTION;
55 flushi(p->addr);
54} 56}
55 57
56/* kprobe_status settings */ 58void arch_disarm_kprobe(struct kprobe *p)
57#define KPROBE_HIT_ACTIVE 0x00000001 59{
58#define KPROBE_HIT_SS 0x00000002 60 *p->addr = p->opcode;
61 flushi(p->addr);
62}
63
64void arch_remove_kprobe(struct kprobe *p)
65{
66}
59 67
60static struct kprobe *current_kprobe; 68static struct kprobe *current_kprobe;
61static unsigned long current_kprobe_orig_tnpc; 69static unsigned long current_kprobe_orig_tnpc;
62static unsigned long current_kprobe_orig_tstate_pil; 70static unsigned long current_kprobe_orig_tstate_pil;
63static unsigned int kprobe_status; 71static unsigned int kprobe_status;
72static struct kprobe *kprobe_prev;
73static unsigned long kprobe_orig_tnpc_prev;
74static unsigned long kprobe_orig_tstate_pil_prev;
75static unsigned int kprobe_status_prev;
64 76
65static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) 77static inline void save_previous_kprobe(void)
78{
79 kprobe_status_prev = kprobe_status;
80 kprobe_orig_tnpc_prev = current_kprobe_orig_tnpc;
81 kprobe_orig_tstate_pil_prev = current_kprobe_orig_tstate_pil;
82 kprobe_prev = current_kprobe;
83}
84
85static inline void restore_previous_kprobe(void)
86{
87 kprobe_status = kprobe_status_prev;
88 current_kprobe_orig_tnpc = kprobe_orig_tnpc_prev;
89 current_kprobe_orig_tstate_pil = kprobe_orig_tstate_pil_prev;
90 current_kprobe = kprobe_prev;
91}
92
93static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs)
66{ 94{
67 current_kprobe_orig_tnpc = regs->tnpc; 95 current_kprobe_orig_tnpc = regs->tnpc;
68 current_kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL); 96 current_kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL);
97 current_kprobe = p;
98}
99
100static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
101{
69 regs->tstate |= TSTATE_PIL; 102 regs->tstate |= TSTATE_PIL;
70 103
71 /*single step inline, if it a breakpoint instruction*/ 104 /*single step inline, if it a breakpoint instruction*/
@@ -78,17 +111,6 @@ static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
78 } 111 }
79} 112}
80 113
81static 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
92static int kprobe_handler(struct pt_regs *regs) 114static int kprobe_handler(struct pt_regs *regs)
93{ 115{
94 struct kprobe *p; 116 struct kprobe *p;
@@ -109,8 +131,18 @@ static int kprobe_handler(struct pt_regs *regs)
109 unlock_kprobes(); 131 unlock_kprobes();
110 goto no_kprobe; 132 goto no_kprobe;
111 } 133 }
112 disarm_kprobe(p, regs); 134 /* We have reentered the kprobe_handler(), since
113 ret = 1; 135 * another probe was hit while within the handler.
136 * We here save the original kprobes variables and
137 * just single step on the instruction of the new probe
138 * without calling any user handlers.
139 */
140 save_previous_kprobe();
141 set_current_kprobe(p, regs);
142 p->nmissed++;
143 kprobe_status = KPROBE_REENTER;
144 prepare_singlestep(p, regs);
145 return 1;
114 } else { 146 } else {
115 p = current_kprobe; 147 p = current_kprobe;
116 if (p->break_handler && p->break_handler(p, regs)) 148 if (p->break_handler && p->break_handler(p, regs))
@@ -138,8 +170,8 @@ static int kprobe_handler(struct pt_regs *regs)
138 goto no_kprobe; 170 goto no_kprobe;
139 } 171 }
140 172
173 set_current_kprobe(p, regs);
141 kprobe_status = KPROBE_HIT_ACTIVE; 174 kprobe_status = KPROBE_HIT_ACTIVE;
142 current_kprobe = p;
143 if (p->pre_handler && p->pre_handler(p, regs)) 175 if (p->pre_handler && p->pre_handler(p, regs))
144 return 1; 176 return 1;
145 177
@@ -245,12 +277,20 @@ static inline int post_kprobe_handler(struct pt_regs *regs)
245 if (!kprobe_running()) 277 if (!kprobe_running())
246 return 0; 278 return 0;
247 279
248 if (current_kprobe->post_handler) 280 if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) {
281 kprobe_status = KPROBE_HIT_SSDONE;
249 current_kprobe->post_handler(current_kprobe, regs, 0); 282 current_kprobe->post_handler(current_kprobe, regs, 0);
283 }
250 284
251 resume_execution(current_kprobe, regs); 285 resume_execution(current_kprobe, regs);
252 286
287 /*Restore back the original saved kprobes variables and continue. */
288 if (kprobe_status == KPROBE_REENTER) {
289 restore_previous_kprobe();
290 goto out;
291 }
253 unlock_kprobes(); 292 unlock_kprobes();
293out:
254 preempt_enable_no_resched(); 294 preempt_enable_no_resched();
255 295
256 return 1; 296 return 1;
@@ -392,3 +432,4 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
392 } 432 }
393 return 0; 433 return 0;
394} 434}
435