diff options
| -rw-r--r-- | arch/sparc64/kernel/kprobes.c | 91 | ||||
| -rw-r--r-- | include/asm-sparc64/kprobes.h | 11 |
2 files changed, 49 insertions, 53 deletions
diff --git a/arch/sparc64/kernel/kprobes.c b/arch/sparc64/kernel/kprobes.c index 8e75ed762fd8..ae221f0d4a6f 100644 --- a/arch/sparc64/kernel/kprobes.c +++ b/arch/sparc64/kernel/kprobes.c | |||
| @@ -45,7 +45,11 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); | |||
| 45 | int __kprobes arch_prepare_kprobe(struct kprobe *p) | 45 | int __kprobes arch_prepare_kprobe(struct kprobe *p) |
| 46 | { | 46 | { |
| 47 | p->ainsn.insn[0] = *p->addr; | 47 | p->ainsn.insn[0] = *p->addr; |
| 48 | flushi(&p->ainsn.insn[0]); | ||
| 49 | |||
| 48 | p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2; | 50 | p->ainsn.insn[1] = BREAKPOINT_INSTRUCTION_2; |
| 51 | flushi(&p->ainsn.insn[1]); | ||
| 52 | |||
| 49 | p->opcode = *p->addr; | 53 | p->opcode = *p->addr; |
| 50 | return 0; | 54 | return 0; |
| 51 | } | 55 | } |
| @@ -185,16 +189,19 @@ no_kprobe: | |||
| 185 | /* If INSN is a relative control transfer instruction, | 189 | /* If INSN is a relative control transfer instruction, |
| 186 | * return the corrected branch destination value. | 190 | * return the corrected branch destination value. |
| 187 | * | 191 | * |
| 188 | * The original INSN location was REAL_PC, it actually | 192 | * regs->tpc and regs->tnpc still hold the values of the |
| 189 | * executed at PC and produced destination address NPC. | 193 | * program counters at the time of trap due to the execution |
| 194 | * of the BREAKPOINT_INSTRUCTION_2 at p->ainsn.insn[1] | ||
| 195 | * | ||
| 190 | */ | 196 | */ |
| 191 | static unsigned long __kprobes relbranch_fixup(u32 insn, unsigned long real_pc, | 197 | static unsigned long __kprobes relbranch_fixup(u32 insn, struct kprobe *p, |
| 192 | unsigned long pc, | 198 | struct pt_regs *regs) |
| 193 | unsigned long npc) | ||
| 194 | { | 199 | { |
| 200 | unsigned long real_pc = (unsigned long) p->addr; | ||
| 201 | |||
| 195 | /* Branch not taken, no mods necessary. */ | 202 | /* Branch not taken, no mods necessary. */ |
| 196 | if (npc == pc + 0x4UL) | 203 | if (regs->tnpc == regs->tpc + 0x4UL) |
| 197 | return real_pc + 0x4UL; | 204 | return real_pc + 0x8UL; |
| 198 | 205 | ||
| 199 | /* The three cases are call, branch w/prediction, | 206 | /* The three cases are call, branch w/prediction, |
| 200 | * and traditional branch. | 207 | * and traditional branch. |
| @@ -202,14 +209,21 @@ static unsigned long __kprobes relbranch_fixup(u32 insn, unsigned long real_pc, | |||
| 202 | if ((insn & 0xc0000000) == 0x40000000 || | 209 | if ((insn & 0xc0000000) == 0x40000000 || |
| 203 | (insn & 0xc1c00000) == 0x00400000 || | 210 | (insn & 0xc1c00000) == 0x00400000 || |
| 204 | (insn & 0xc1c00000) == 0x00800000) { | 211 | (insn & 0xc1c00000) == 0x00800000) { |
| 212 | unsigned long ainsn_addr; | ||
| 213 | |||
| 214 | ainsn_addr = (unsigned long) &p->ainsn.insn[0]; | ||
| 215 | |||
| 205 | /* The instruction did all the work for us | 216 | /* The instruction did all the work for us |
| 206 | * already, just apply the offset to the correct | 217 | * already, just apply the offset to the correct |
| 207 | * instruction location. | 218 | * instruction location. |
| 208 | */ | 219 | */ |
| 209 | return (real_pc + (npc - pc)); | 220 | return (real_pc + (regs->tnpc - ainsn_addr)); |
| 210 | } | 221 | } |
| 211 | 222 | ||
| 212 | return real_pc + 0x4UL; | 223 | /* It is jmpl or some other absolute PC modification instruction, |
| 224 | * leave NPC as-is. | ||
| 225 | */ | ||
| 226 | return regs->tnpc; | ||
| 213 | } | 227 | } |
| 214 | 228 | ||
| 215 | /* If INSN is an instruction which writes it's PC location | 229 | /* If INSN is an instruction which writes it's PC location |
| @@ -220,12 +234,12 @@ static void __kprobes retpc_fixup(struct pt_regs *regs, u32 insn, | |||
| 220 | { | 234 | { |
| 221 | unsigned long *slot = NULL; | 235 | unsigned long *slot = NULL; |
| 222 | 236 | ||
| 223 | /* Simplest cast is call, which always uses %o7 */ | 237 | /* Simplest case is 'call', which always uses %o7 */ |
| 224 | if ((insn & 0xc0000000) == 0x40000000) { | 238 | if ((insn & 0xc0000000) == 0x40000000) { |
| 225 | slot = ®s->u_regs[UREG_I7]; | 239 | slot = ®s->u_regs[UREG_I7]; |
| 226 | } | 240 | } |
| 227 | 241 | ||
| 228 | /* Jmpl encodes the register inside of the opcode */ | 242 | /* 'jmpl' encodes the register inside of the opcode */ |
| 229 | if ((insn & 0xc1f80000) == 0x81c00000) { | 243 | if ((insn & 0xc1f80000) == 0x81c00000) { |
| 230 | unsigned long rd = ((insn >> 25) & 0x1f); | 244 | unsigned long rd = ((insn >> 25) & 0x1f); |
| 231 | 245 | ||
| @@ -247,11 +261,11 @@ static void __kprobes retpc_fixup(struct pt_regs *regs, u32 insn, | |||
| 247 | 261 | ||
| 248 | /* | 262 | /* |
| 249 | * Called after single-stepping. p->addr is the address of the | 263 | * Called after single-stepping. p->addr is the address of the |
| 250 | * instruction whose first byte has been replaced by the breakpoint | 264 | * instruction which has been replaced by the breakpoint |
| 251 | * instruction. To avoid the SMP problems that can occur when we | 265 | * instruction. To avoid the SMP problems that can occur when we |
| 252 | * temporarily put back the original opcode to single-step, we | 266 | * temporarily put back the original opcode to single-step, we |
| 253 | * single-stepped a copy of the instruction. The address of this | 267 | * single-stepped a copy of the instruction. The address of this |
| 254 | * copy is p->ainsn.insn. | 268 | * copy is &p->ainsn.insn[0]. |
| 255 | * | 269 | * |
| 256 | * This function prepares to return from the post-single-step | 270 | * This function prepares to return from the post-single-step |
| 257 | * breakpoint trap. | 271 | * breakpoint trap. |
| @@ -261,11 +275,11 @@ static void __kprobes resume_execution(struct kprobe *p, | |||
| 261 | { | 275 | { |
| 262 | u32 insn = p->ainsn.insn[0]; | 276 | u32 insn = p->ainsn.insn[0]; |
| 263 | 277 | ||
| 278 | regs->tnpc = relbranch_fixup(insn, p, regs); | ||
| 279 | |||
| 280 | /* This assignment must occur after relbranch_fixup() */ | ||
| 264 | regs->tpc = kcb->kprobe_orig_tnpc; | 281 | regs->tpc = kcb->kprobe_orig_tnpc; |
| 265 | regs->tnpc = relbranch_fixup(insn, | 282 | |
| 266 | (unsigned long) p->addr, | ||
| 267 | (unsigned long) &p->ainsn.insn[0], | ||
| 268 | regs->tnpc); | ||
| 269 | retpc_fixup(regs, insn, (unsigned long) p->addr); | 283 | retpc_fixup(regs, insn, (unsigned long) p->addr); |
| 270 | 284 | ||
| 271 | regs->tstate = ((regs->tstate & ~TSTATE_PIL) | | 285 | regs->tstate = ((regs->tstate & ~TSTATE_PIL) | |
| @@ -430,17 +444,8 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) | |||
| 430 | struct jprobe *jp = container_of(p, struct jprobe, kp); | 444 | struct jprobe *jp = container_of(p, struct jprobe, kp); |
| 431 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 445 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
| 432 | 446 | ||
| 433 | kcb->jprobe_saved_regs_location = regs; | ||
| 434 | memcpy(&(kcb->jprobe_saved_regs), regs, sizeof(*regs)); | 447 | memcpy(&(kcb->jprobe_saved_regs), regs, sizeof(*regs)); |
| 435 | 448 | ||
| 436 | /* Save a whole stack frame, this gets arguments | ||
| 437 | * pushed onto the stack after using up all the | ||
| 438 | * arg registers. | ||
| 439 | */ | ||
| 440 | memcpy(&(kcb->jprobe_saved_stack), | ||
| 441 | (char *) (regs->u_regs[UREG_FP] + STACK_BIAS), | ||
| 442 | sizeof(kcb->jprobe_saved_stack)); | ||
| 443 | |||
| 444 | regs->tpc = (unsigned long) jp->entry; | 449 | regs->tpc = (unsigned long) jp->entry; |
| 445 | regs->tnpc = ((unsigned long) jp->entry) + 0x4UL; | 450 | regs->tnpc = ((unsigned long) jp->entry) + 0x4UL; |
| 446 | regs->tstate |= TSTATE_PIL; | 451 | regs->tstate |= TSTATE_PIL; |
| @@ -450,10 +455,19 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) | |||
| 450 | 455 | ||
| 451 | void __kprobes jprobe_return(void) | 456 | void __kprobes jprobe_return(void) |
| 452 | { | 457 | { |
| 453 | __asm__ __volatile__( | 458 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
| 454 | ".globl jprobe_return_trap_instruction\n" | 459 | register unsigned long orig_fp asm("g1"); |
| 460 | |||
| 461 | orig_fp = kcb->jprobe_saved_regs.u_regs[UREG_FP]; | ||
| 462 | __asm__ __volatile__("\n" | ||
| 463 | "1: cmp %%sp, %0\n\t" | ||
| 464 | "blu,a,pt %%xcc, 1b\n\t" | ||
| 465 | " restore\n\t" | ||
| 466 | ".globl jprobe_return_trap_instruction\n" | ||
| 455 | "jprobe_return_trap_instruction:\n\t" | 467 | "jprobe_return_trap_instruction:\n\t" |
| 456 | "ta 0x70"); | 468 | "ta 0x70" |
| 469 | : /* no outputs */ | ||
| 470 | : "r" (orig_fp)); | ||
| 457 | } | 471 | } |
| 458 | 472 | ||
| 459 | extern void jprobe_return_trap_instruction(void); | 473 | extern void jprobe_return_trap_instruction(void); |
| @@ -466,26 +480,7 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | |||
| 466 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 480 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
| 467 | 481 | ||
| 468 | if (addr == (u32 *) jprobe_return_trap_instruction) { | 482 | if (addr == (u32 *) jprobe_return_trap_instruction) { |
| 469 | if (kcb->jprobe_saved_regs_location != regs) { | ||
| 470 | printk("JPROBE: Current regs (%p) does not match " | ||
| 471 | "saved regs (%p).\n", | ||
| 472 | regs, kcb->jprobe_saved_regs_location); | ||
| 473 | printk("JPROBE: Saved registers\n"); | ||
| 474 | __show_regs(kcb->jprobe_saved_regs_location); | ||
| 475 | printk("JPROBE: Current registers\n"); | ||
| 476 | __show_regs(regs); | ||
| 477 | BUG(); | ||
| 478 | } | ||
| 479 | /* Restore old register state. Do pt_regs | ||
| 480 | * first so that UREG_FP is the original one for | ||
| 481 | * the stack frame restore. | ||
| 482 | */ | ||
| 483 | memcpy(regs, &(kcb->jprobe_saved_regs), sizeof(*regs)); | 483 | memcpy(regs, &(kcb->jprobe_saved_regs), sizeof(*regs)); |
| 484 | |||
| 485 | memcpy((char *) (regs->u_regs[UREG_FP] + STACK_BIAS), | ||
| 486 | &(kcb->jprobe_saved_stack), | ||
| 487 | sizeof(kcb->jprobe_saved_stack)); | ||
| 488 | |||
| 489 | preempt_enable_no_resched(); | 484 | preempt_enable_no_resched(); |
| 490 | return 1; | 485 | return 1; |
| 491 | } | 486 | } |
diff --git a/include/asm-sparc64/kprobes.h b/include/asm-sparc64/kprobes.h index c9f5c34d318c..becc38fa06c5 100644 --- a/include/asm-sparc64/kprobes.h +++ b/include/asm-sparc64/kprobes.h | |||
| @@ -13,7 +13,11 @@ typedef u32 kprobe_opcode_t; | |||
| 13 | #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry | 13 | #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry |
| 14 | #define arch_remove_kprobe(p) do {} while (0) | 14 | #define arch_remove_kprobe(p) do {} while (0) |
| 15 | #define ARCH_INACTIVE_KPROBE_COUNT 0 | 15 | #define ARCH_INACTIVE_KPROBE_COUNT 0 |
| 16 | #define flush_insn_slot(p) do { } while (0) | 16 | |
| 17 | #define flush_insn_slot(p) \ | ||
| 18 | do { flushi(&(p)->ainsn.insn[0]); \ | ||
| 19 | flushi(&(p)->ainsn.insn[1]); \ | ||
| 20 | } while (0) | ||
| 17 | 21 | ||
| 18 | /* Architecture specific copy of original instruction*/ | 22 | /* Architecture specific copy of original instruction*/ |
| 19 | struct arch_specific_insn { | 23 | struct arch_specific_insn { |
| @@ -23,7 +27,7 @@ struct arch_specific_insn { | |||
| 23 | 27 | ||
| 24 | struct prev_kprobe { | 28 | struct prev_kprobe { |
| 25 | struct kprobe *kp; | 29 | struct kprobe *kp; |
| 26 | unsigned int status; | 30 | unsigned long status; |
| 27 | unsigned long orig_tnpc; | 31 | unsigned long orig_tnpc; |
| 28 | unsigned long orig_tstate_pil; | 32 | unsigned long orig_tstate_pil; |
| 29 | }; | 33 | }; |
| @@ -33,10 +37,7 @@ struct kprobe_ctlblk { | |||
| 33 | unsigned long kprobe_status; | 37 | unsigned long kprobe_status; |
| 34 | unsigned long kprobe_orig_tnpc; | 38 | unsigned long kprobe_orig_tnpc; |
| 35 | unsigned long kprobe_orig_tstate_pil; | 39 | unsigned long kprobe_orig_tstate_pil; |
| 36 | long *jprobe_saved_esp; | ||
| 37 | struct pt_regs jprobe_saved_regs; | 40 | struct pt_regs jprobe_saved_regs; |
| 38 | struct pt_regs *jprobe_saved_regs_location; | ||
| 39 | struct sparc_stackf jprobe_saved_stack; | ||
| 40 | struct prev_kprobe prev_kprobe; | 41 | struct prev_kprobe prev_kprobe; |
| 41 | }; | 42 | }; |
| 42 | 43 | ||
