diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2011-01-05 06:47:17 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2011-01-05 06:47:23 -0500 |
commit | fc0a1fea6b81095b6c0e01ec3407d04c8341974c (patch) | |
tree | da864df5bdb576401afb9531f171ee31d268ddbb /arch/s390/kernel/kprobes.c | |
parent | 35f2aaa79a2d484c8449f34461464a1e84e36e2b (diff) |
[S390] kprobes: single step cleanup
The saved interrupt mask and the saved control registers are only
relevant while single stepping is set up. A secondary kprobe while
kprobe single stepping is active may not occur. That makes is safe
to remove the save and restore of kprobe_saved_imask / kprobe_save_ctl
from save_previous_kprobe and restore_previous_kprobe.
Move all single step related code to two functions, enable_singlestep
and disable_singlestep.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/kprobes.c')
-rw-r--r-- | arch/s390/kernel/kprobes.c | 77 |
1 files changed, 36 insertions, 41 deletions
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index b8e51759b6e4..91c611f84ff7 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c | |||
@@ -198,51 +198,58 @@ void __kprobes arch_remove_kprobe(struct kprobe *p) | |||
198 | } | 198 | } |
199 | } | 199 | } |
200 | 200 | ||
201 | static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) | 201 | static void __kprobes enable_singlestep(struct kprobe_ctlblk *kcb, |
202 | struct pt_regs *regs, | ||
203 | unsigned long ip) | ||
202 | { | 204 | { |
203 | per_cr_bits kprobe_per_regs[1]; | 205 | per_cr_bits kprobe_per_regs[1]; |
204 | 206 | ||
205 | memset(kprobe_per_regs, 0, sizeof(per_cr_bits)); | ||
206 | regs->psw.addr = (unsigned long)p->ainsn.insn | PSW_ADDR_AMODE; | ||
207 | |||
208 | /* Set up the per control reg info, will pass to lctl */ | 207 | /* Set up the per control reg info, will pass to lctl */ |
208 | memset(kprobe_per_regs, 0, sizeof(per_cr_bits)); | ||
209 | kprobe_per_regs[0].em_instruction_fetch = 1; | 209 | kprobe_per_regs[0].em_instruction_fetch = 1; |
210 | kprobe_per_regs[0].starting_addr = (unsigned long)p->ainsn.insn; | 210 | kprobe_per_regs[0].starting_addr = ip; |
211 | kprobe_per_regs[0].ending_addr = (unsigned long)p->ainsn.insn + 1; | 211 | kprobe_per_regs[0].ending_addr = ip; |
212 | 212 | ||
213 | /* Set the PER control regs, turns on single step for this address */ | 213 | /* Save control regs and psw mask */ |
214 | __ctl_store(kcb->kprobe_saved_ctl, 9, 11); | ||
215 | kcb->kprobe_saved_imask = regs->psw.mask & | ||
216 | (PSW_MASK_PER | PSW_MASK_IO | PSW_MASK_EXT); | ||
217 | |||
218 | /* Set PER control regs, turns on single step for the given address */ | ||
214 | __ctl_load(kprobe_per_regs, 9, 11); | 219 | __ctl_load(kprobe_per_regs, 9, 11); |
215 | regs->psw.mask |= PSW_MASK_PER; | 220 | regs->psw.mask |= PSW_MASK_PER; |
216 | regs->psw.mask &= ~(PSW_MASK_IO | PSW_MASK_EXT); | 221 | regs->psw.mask &= ~(PSW_MASK_IO | PSW_MASK_EXT); |
222 | regs->psw.addr = ip | PSW_ADDR_AMODE; | ||
217 | } | 223 | } |
218 | 224 | ||
225 | static void __kprobes disable_singlestep(struct kprobe_ctlblk *kcb, | ||
226 | struct pt_regs *regs, | ||
227 | unsigned long ip) | ||
228 | { | ||
229 | /* Restore control regs and psw mask, set new psw address */ | ||
230 | __ctl_load(kcb->kprobe_saved_ctl, 9, 11); | ||
231 | regs->psw.mask &= ~PSW_MASK_PER; | ||
232 | regs->psw.mask |= kcb->kprobe_saved_imask; | ||
233 | regs->psw.addr = ip | PSW_ADDR_AMODE; | ||
234 | } | ||
235 | |||
236 | |||
219 | static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) | 237 | static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) |
220 | { | 238 | { |
221 | kcb->prev_kprobe.kp = kprobe_running(); | 239 | kcb->prev_kprobe.kp = kprobe_running(); |
222 | kcb->prev_kprobe.status = kcb->kprobe_status; | 240 | kcb->prev_kprobe.status = kcb->kprobe_status; |
223 | kcb->prev_kprobe.kprobe_saved_imask = kcb->kprobe_saved_imask; | ||
224 | memcpy(kcb->prev_kprobe.kprobe_saved_ctl, kcb->kprobe_saved_ctl, | ||
225 | sizeof(kcb->kprobe_saved_ctl)); | ||
226 | } | 241 | } |
227 | 242 | ||
228 | static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) | 243 | static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) |
229 | { | 244 | { |
230 | __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; | 245 | __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; |
231 | kcb->kprobe_status = kcb->prev_kprobe.status; | 246 | kcb->kprobe_status = kcb->prev_kprobe.status; |
232 | kcb->kprobe_saved_imask = kcb->prev_kprobe.kprobe_saved_imask; | ||
233 | memcpy(kcb->kprobe_saved_ctl, kcb->prev_kprobe.kprobe_saved_ctl, | ||
234 | sizeof(kcb->kprobe_saved_ctl)); | ||
235 | } | 247 | } |
236 | 248 | ||
237 | static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, | 249 | static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, |
238 | struct kprobe_ctlblk *kcb) | 250 | struct kprobe_ctlblk *kcb) |
239 | { | 251 | { |
240 | __get_cpu_var(current_kprobe) = p; | 252 | __get_cpu_var(current_kprobe) = p; |
241 | /* Save the interrupt and per flags */ | ||
242 | kcb->kprobe_saved_imask = regs->psw.mask & | ||
243 | (PSW_MASK_PER | PSW_MASK_IO | PSW_MASK_EXT); | ||
244 | /* Save the control regs that govern PER */ | ||
245 | __ctl_store(kcb->kprobe_saved_ctl, 9, 11); | ||
246 | } | 253 | } |
247 | 254 | ||
248 | void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, | 255 | void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, |
@@ -282,7 +289,8 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) | |||
282 | save_previous_kprobe(kcb); | 289 | save_previous_kprobe(kcb); |
283 | set_current_kprobe(p, regs, kcb); | 290 | set_current_kprobe(p, regs, kcb); |
284 | kprobes_inc_nmissed_count(p); | 291 | kprobes_inc_nmissed_count(p); |
285 | prepare_singlestep(p, regs); | 292 | enable_singlestep(kcb, regs, |
293 | (unsigned long) p->ainsn.insn); | ||
286 | kcb->kprobe_status = KPROBE_REENTER; | 294 | kcb->kprobe_status = KPROBE_REENTER; |
287 | return 1; | 295 | return 1; |
288 | } else { | 296 | } else { |
@@ -311,7 +319,7 @@ static int __kprobes kprobe_handler(struct pt_regs *regs) | |||
311 | return 1; | 319 | return 1; |
312 | 320 | ||
313 | ss_probe: | 321 | ss_probe: |
314 | prepare_singlestep(p, regs); | 322 | enable_singlestep(kcb, regs, (unsigned long) p->ainsn.insn); |
315 | kcb->kprobe_status = KPROBE_HIT_SS; | 323 | kcb->kprobe_status = KPROBE_HIT_SS; |
316 | return 1; | 324 | return 1; |
317 | 325 | ||
@@ -433,31 +441,20 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, | |||
433 | static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) | 441 | static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) |
434 | { | 442 | { |
435 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 443 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
436 | 444 | unsigned long ip = regs->psw.addr & PSW_ADDR_INSN; | |
437 | regs->psw.addr &= PSW_ADDR_INSN; | ||
438 | 445 | ||
439 | if (p->ainsn.fixup & FIXUP_PSW_NORMAL) | 446 | if (p->ainsn.fixup & FIXUP_PSW_NORMAL) |
440 | regs->psw.addr = (unsigned long)p->addr + | 447 | ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn; |
441 | ((unsigned long)regs->psw.addr - | ||
442 | (unsigned long)p->ainsn.insn); | ||
443 | 448 | ||
444 | if (p->ainsn.fixup & FIXUP_BRANCH_NOT_TAKEN) | 449 | if (p->ainsn.fixup & FIXUP_BRANCH_NOT_TAKEN) |
445 | if ((unsigned long)regs->psw.addr - | 450 | if (ip - (unsigned long) p->ainsn.insn == p->ainsn.ilen) |
446 | (unsigned long)p->ainsn.insn == p->ainsn.ilen) | 451 | ip = (unsigned long) p->addr + p->ainsn.ilen; |
447 | regs->psw.addr = (unsigned long)p->addr + p->ainsn.ilen; | ||
448 | 452 | ||
449 | if (p->ainsn.fixup & FIXUP_RETURN_REGISTER) | 453 | if (p->ainsn.fixup & FIXUP_RETURN_REGISTER) |
450 | regs->gprs[p->ainsn.reg] = ((unsigned long)p->addr + | 454 | regs->gprs[p->ainsn.reg] += (unsigned long) p->addr - |
451 | (regs->gprs[p->ainsn.reg] - | 455 | (unsigned long) p->ainsn.insn; |
452 | (unsigned long)p->ainsn.insn)) | ||
453 | | PSW_ADDR_AMODE; | ||
454 | 456 | ||
455 | regs->psw.addr |= PSW_ADDR_AMODE; | 457 | disable_singlestep(kcb, regs, ip); |
456 | /* turn off PER mode */ | ||
457 | regs->psw.mask &= ~PSW_MASK_PER; | ||
458 | /* Restore the original per control regs */ | ||
459 | __ctl_load(kcb->kprobe_saved_ctl, 9, 11); | ||
460 | regs->psw.mask |= kcb->kprobe_saved_imask; | ||
461 | } | 458 | } |
462 | 459 | ||
463 | static int __kprobes post_kprobe_handler(struct pt_regs *regs) | 460 | static int __kprobes post_kprobe_handler(struct pt_regs *regs) |
@@ -515,9 +512,7 @@ static int __kprobes kprobe_trap_handler(struct pt_regs *regs, int trapnr) | |||
515 | * and allow the page fault handler to continue as a | 512 | * and allow the page fault handler to continue as a |
516 | * normal page fault. | 513 | * normal page fault. |
517 | */ | 514 | */ |
518 | regs->psw.addr = (unsigned long)cur->addr | PSW_ADDR_AMODE; | 515 | disable_singlestep(kcb, regs, (unsigned long) cur->addr); |
519 | regs->psw.mask &= ~PSW_MASK_PER; | ||
520 | regs->psw.mask |= kcb->kprobe_saved_imask; | ||
521 | if (kcb->kprobe_status == KPROBE_REENTER) | 516 | if (kcb->kprobe_status == KPROBE_REENTER) |
522 | restore_previous_kprobe(kcb); | 517 | restore_previous_kprobe(kcb); |
523 | else { | 518 | else { |