diff options
Diffstat (limited to 'arch/sparc64/kernel/kprobes.c')
-rw-r--r-- | arch/sparc64/kernel/kprobes.c | 83 |
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 | ||
52 | void arch_remove_kprobe(struct kprobe *p) | 52 | void 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 */ | 58 | void 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 | |||
64 | void arch_remove_kprobe(struct kprobe *p) | ||
65 | { | ||
66 | } | ||
59 | 67 | ||
60 | static struct kprobe *current_kprobe; | 68 | static struct kprobe *current_kprobe; |
61 | static unsigned long current_kprobe_orig_tnpc; | 69 | static unsigned long current_kprobe_orig_tnpc; |
62 | static unsigned long current_kprobe_orig_tstate_pil; | 70 | static unsigned long current_kprobe_orig_tstate_pil; |
63 | static unsigned int kprobe_status; | 71 | static unsigned int kprobe_status; |
72 | static struct kprobe *kprobe_prev; | ||
73 | static unsigned long kprobe_orig_tnpc_prev; | ||
74 | static unsigned long kprobe_orig_tstate_pil_prev; | ||
75 | static unsigned int kprobe_status_prev; | ||
64 | 76 | ||
65 | static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 77 | static 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 | |||
85 | static 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 | |||
93 | static 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 | |||
100 | static 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 | ||
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) | 114 | static 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(); |
293 | out: | ||
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 | |||