]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - arch/powerpc/kernel/kprobes.c
powerpc/kprobes: Emulate instructions on kprobe handler re-entry
[linux.git] / arch / powerpc / kernel / kprobes.c
index fce05a38851c7f1f6e4458e93bdd3041e99cd2cd..b71922618ed25f1d9e6fc8b257cb6554de84e2b1 100644 (file)
@@ -42,6 +42,64 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 
 struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
 
+kprobe_opcode_t *kprobe_lookup_name(const char *name, unsigned int offset)
+{
+       kprobe_opcode_t *addr;
+
+#ifdef PPC64_ELF_ABI_v2
+       /* PPC64 ABIv2 needs local entry point */
+       addr = (kprobe_opcode_t *)kallsyms_lookup_name(name);
+       if (addr && !offset)
+               addr = (kprobe_opcode_t *)ppc_function_entry(addr);
+#elif defined(PPC64_ELF_ABI_v1)
+       /*
+        * 64bit powerpc ABIv1 uses function descriptors:
+        * - Check for the dot variant of the symbol first.
+        * - If that fails, try looking up the symbol provided.
+        *
+        * This ensures we always get to the actual symbol and not
+        * the descriptor.
+        *
+        * Also handle <module:symbol> format.
+        */
+       char dot_name[MODULE_NAME_LEN + 1 + KSYM_NAME_LEN];
+       const char *modsym;
+       bool dot_appended = false;
+       if ((modsym = strchr(name, ':')) != NULL) {
+               modsym++;
+               if (*modsym != '\0' && *modsym != '.') {
+                       /* Convert to <module:.symbol> */
+                       strncpy(dot_name, name, modsym - name);
+                       dot_name[modsym - name] = '.';
+                       dot_name[modsym - name + 1] = '\0';
+                       strncat(dot_name, modsym,
+                               sizeof(dot_name) - (modsym - name) - 2);
+                       dot_appended = true;
+               } else {
+                       dot_name[0] = '\0';
+                       strncat(dot_name, name, sizeof(dot_name) - 1);
+               }
+       } else if (name[0] != '.') {
+               dot_name[0] = '.';
+               dot_name[1] = '\0';
+               strncat(dot_name, name, KSYM_NAME_LEN - 2);
+               dot_appended = true;
+       } else {
+               dot_name[0] = '\0';
+               strncat(dot_name, name, KSYM_NAME_LEN - 1);
+       }
+       addr = (kprobe_opcode_t *)kallsyms_lookup_name(dot_name);
+       if (!addr && dot_appended) {
+               /* Let's try the original non-dot symbol lookup */
+               addr = (kprobe_opcode_t *)kallsyms_lookup_name(name);
+       }
+#else
+       addr = (kprobe_opcode_t *)kallsyms_lookup_name(name);
+#endif
+
+       return addr;
+}
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
        int ret = 0;
@@ -131,6 +189,15 @@ static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
        kcb->kprobe_saved_msr = regs->msr;
 }
 
+bool arch_function_offset_within_entry(unsigned long offset)
+{
+#ifdef PPC64_ELF_ABI_v2
+       return offset <= 8;
+#else
+       return !offset;
+#endif
+}
+
 void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
                                      struct pt_regs *regs)
 {
@@ -140,6 +207,35 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
        regs->link = (unsigned long)kretprobe_trampoline;
 }
 
+int __kprobes try_to_emulate(struct kprobe *p, struct pt_regs *regs)
+{
+       int ret;
+       unsigned int insn = *p->ainsn.insn;
+
+       /* regs->nip is also adjusted if emulate_step returns 1 */
+       ret = emulate_step(regs, insn);
+       if (ret > 0) {
+               /*
+                * Once this instruction has been boosted
+                * successfully, set the boostable flag
+                */
+               if (unlikely(p->ainsn.boostable == 0))
+                       p->ainsn.boostable = 1;
+       } else if (ret < 0) {
+               /*
+                * We don't allow kprobes on mtmsr(d)/rfi(d), etc.
+                * So, we should never get here... but, its still
+                * good to catch them, just in case...
+                */
+               printk("Can't step on instruction %x\n", insn);
+               BUG();
+       } else if (ret == 0)
+               /* This instruction can't be boosted */
+               p->ainsn.boostable = -1;
+
+       return ret;
+}
+
 int __kprobes kprobe_handler(struct pt_regs *regs)
 {
        struct kprobe *p;
@@ -181,6 +277,14 @@ int __kprobes kprobe_handler(struct pt_regs *regs)
                        kprobes_inc_nmissed_count(p);
                        prepare_singlestep(p, regs);
                        kcb->kprobe_status = KPROBE_REENTER;
+                       if (p->ainsn.boostable >= 0) {
+                               ret = try_to_emulate(p, regs);
+
+                               if (ret > 0) {
+                                       restore_previous_kprobe(kcb);
+                                       return 1;
+                               }
+                       }
                        return 1;
                } else {
                        if (*addr != BREAKPOINT_INSTRUCTION) {
@@ -235,18 +339,9 @@ int __kprobes kprobe_handler(struct pt_regs *regs)
 
 ss_probe:
        if (p->ainsn.boostable >= 0) {
-               unsigned int insn = *p->ainsn.insn;
+               ret = try_to_emulate(p, regs);
 
-               /* regs->nip is also adjusted if emulate_step returns 1 */
-               ret = emulate_step(regs, insn);
                if (ret > 0) {
-                       /*
-                        * Once this instruction has been boosted
-                        * successfully, set the boostable flag
-                        */
-                       if (unlikely(p->ainsn.boostable == 0))
-                               p->ainsn.boostable = 1;
-
                        if (p->post_handler)
                                p->post_handler(p, regs, 0);
 
@@ -254,17 +349,7 @@ int __kprobes kprobe_handler(struct pt_regs *regs)
                        reset_current_kprobe();
                        preempt_enable_no_resched();
                        return 1;
-               } else if (ret < 0) {
-                       /*
-                        * We don't allow kprobes on mtmsr(d)/rfi(d), etc.
-                        * So, we should never get here... but, its still
-                        * good to catch them, just in case...
-                        */
-                       printk("Can't step on instruction %x\n", insn);
-                       BUG();
-               } else if (ret == 0)
-                       /* This instruction can't be boosted */
-                       p->ainsn.boostable = -1;
+               }
        }
        prepare_singlestep(p, regs);
        kcb->kprobe_status = KPROBE_HIT_SS;