Merge ../linux-2.6-watchdog-mm
[linux-drm-fsl-dcu.git] / arch / i386 / kernel / kprobes.c
index 727e419ad78ac496588ea692acad30766bbad37c..fc79e1e859c498a9d640ba40fcff7494fe6110b6 100644 (file)
@@ -28,7 +28,6 @@
  *             <prasanna@in.ibm.com> added function-return probes.
  */
 
-#include <linux/config.h>
 #include <linux/kprobes.h>
 #include <linux/ptrace.h>
 #include <linux/preempt.h>
@@ -231,20 +230,20 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe *rp,
                                      struct pt_regs *regs)
 {
        unsigned long *sara = (unsigned long *)&regs->esp;
-        struct kretprobe_instance *ri;
 
-        if ((ri = get_free_rp_inst(rp)) != NULL) {
-                ri->rp = rp;
-                ri->task = current;
+       struct kretprobe_instance *ri;
+
+       if ((ri = get_free_rp_inst(rp)) != NULL) {
+               ri->rp = rp;
+               ri->task = current;
                ri->ret_addr = (kprobe_opcode_t *) *sara;
 
                /* Replace the return addr with trampoline addr */
                *sara = (unsigned long) &kretprobe_trampoline;
-
-                add_rp_inst(ri);
-        } else {
-                rp->nmissed++;
-        }
+               add_rp_inst(ri);
+       } else {
+               rp->nmissed++;
+       }
 }
 
 /*
@@ -257,11 +256,6 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
        int ret = 0;
        kprobe_opcode_t *addr;
        struct kprobe_ctlblk *kcb;
-#ifdef CONFIG_PREEMPT
-       unsigned pre_preempt_count = preempt_count();
-#else
-       unsigned pre_preempt_count = 1;
-#endif
 
        addr = (kprobe_opcode_t *)(regs->eip - sizeof(kprobe_opcode_t));
 
@@ -339,13 +333,15 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
                return 1;
 
 ss_probe:
-       if (pre_preempt_count && p->ainsn.boostable == 1 && !p->post_handler){
+#ifndef CONFIG_PREEMPT
+       if (p->ainsn.boostable == 1 && !p->post_handler){
                /* Boost up -- we can execute copied instructions directly */
                reset_current_kprobe();
                regs->eip = (unsigned long)p->ainsn.insn;
                preempt_enable_no_resched();
                return 1;
        }
+#endif
        prepare_singlestep(p, regs);
        kcb->kprobe_status = KPROBE_HIT_SS;
        return 1;
@@ -363,10 +359,13 @@ no_kprobe:
  void __kprobes kretprobe_trampoline_holder(void)
  {
        asm volatile ( ".global kretprobe_trampoline\n"
-                       "kretprobe_trampoline: \n"
+                       "kretprobe_trampoline: \n"
                        "       pushf\n"
-                       /* skip cs, eip, orig_eax, es, ds */
-                       "       subl $20, %esp\n"
+                       /* skip cs, eip, orig_eax */
+                       "       subl $12, %esp\n"
+                       "       pushl %gs\n"
+                       "       pushl %ds\n"
+                       "       pushl %es\n"
                        "       pushl %eax\n"
                        "       pushl %ebp\n"
                        "       pushl %edi\n"
@@ -377,10 +376,10 @@ no_kprobe:
                        "       movl %esp, %eax\n"
                        "       call trampoline_handler\n"
                        /* move eflags to cs */
-                       "       movl 48(%esp), %edx\n"
-                       "       movl %edx, 44(%esp)\n"
+                       "       movl 52(%esp), %edx\n"
+                       "       movl %edx, 48(%esp)\n"
                        /* save true return address on eflags */
-                       "       movl %eax, 48(%esp)\n"
+                       "       movl %eax, 52(%esp)\n"
                        "       popl %ebx\n"
                        "       popl %ecx\n"
                        "       popl %edx\n"
@@ -388,8 +387,8 @@ no_kprobe:
                        "       popl %edi\n"
                        "       popl %ebp\n"
                        "       popl %eax\n"
-                       /* skip eip, orig_eax, es, ds */
-                       "       addl $16, %esp\n"
+                       /* skip eip, orig_eax, es, ds, gs */
+                       "       addl $20, %esp\n"
                        "       popf\n"
                        "       ret\n");
 }
@@ -399,14 +398,19 @@ no_kprobe:
  */
 fastcall void *__kprobes trampoline_handler(struct pt_regs *regs)
 {
-        struct kretprobe_instance *ri = NULL;
-        struct hlist_head *head;
-        struct hlist_node *node, *tmp;
+       struct kretprobe_instance *ri = NULL;
+       struct hlist_head *head, empty_rp;
+       struct hlist_node *node, *tmp;
        unsigned long flags, orig_ret_address = 0;
        unsigned long trampoline_address =(unsigned long)&kretprobe_trampoline;
 
+       INIT_HLIST_HEAD(&empty_rp);
        spin_lock_irqsave(&kretprobe_lock, flags);
-        head = kretprobe_inst_table_head(current);
+       head = kretprobe_inst_table_head(current);
+       /* fixup registers */
+       regs->xcs = __KERNEL_CS;
+       regs->eip = trampoline_address;
+       regs->orig_eax = 0xffffffff;
 
        /*
         * It is possible to have multiple instances associated with a given
@@ -417,23 +421,24 @@ fastcall void *__kprobes trampoline_handler(struct pt_regs *regs)
         * We can handle this because:
         *     - instances are always inserted at the head of the list
         *     - when multiple return probes are registered for the same
-         *       function, the first instance's ret_addr will point to the
+        *       function, the first instance's ret_addr will point to the
         *       real return address, and all the rest will point to
         *       kretprobe_trampoline
         */
        hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
-                if (ri->task != current)
+               if (ri->task != current)
                        /* another task is sharing our hash bucket */
-                        continue;
+                       continue;
 
                if (ri->rp && ri->rp->handler){
                        __get_cpu_var(current_kprobe) = &ri->rp->kp;
+                       get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
                        ri->rp->handler(ri, regs);
                        __get_cpu_var(current_kprobe) = NULL;
                }
 
                orig_ret_address = (unsigned long)ri->ret_addr;
-               recycle_rp_inst(ri);
+               recycle_rp_inst(ri, &empty_rp);
 
                if (orig_ret_address != trampoline_address)
                        /*
@@ -448,6 +453,10 @@ fastcall void *__kprobes trampoline_handler(struct pt_regs *regs)
 
        spin_unlock_irqrestore(&kretprobe_lock, flags);
 
+       hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
+               hlist_del(&ri->hlist);
+               kfree(ri);
+       }
        return (void*)orig_ret_address;
 }