linux-bk/kernel/kprobes.c
<<
>>
Prefs
   1/*
   2 *  Kernel Probes (KProbes)
   3 *  kernel/kprobes.c
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License as published by
   7 * the Free Software Foundation; either version 2 of the License, or
   8 * (at your option) any later version.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software
  17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18 *
  19 * Copyright (C) IBM Corporation, 2002, 2004
  20 *
  21 * 2002-Oct     Created by Vamsi Krishna S <vamsi_krishna@in.ibm.com> Kernel
  22 *              Probes initial implementation (includes suggestions from
  23 *              Rusty Russell).
  24 * 2004-Aug     Updated by Prasanna S Panchamukhi <prasanna@in.ibm.com> with
  25 *              hlists and exceptions notifier as suggested by Andi Kleen.
  26 * 2004-July    Suparna Bhattacharya <suparna@in.ibm.com> added jumper probes
  27 *              interface to access function arguments.
  28 * 2004-Sep     Prasanna S Panchamukhi <prasanna@in.ibm.com> Changed Kprobes
  29 *              exceptions notifier to be first on the priority list.
  30 */
  31#include <linux/kprobes.h>
  32#include <linux/spinlock.h>
  33#include <linux/hash.h>
  34#include <linux/init.h>
  35#include <linux/module.h>
  36#include <asm/cacheflush.h>
  37#include <asm/errno.h>
  38#include <asm/kdebug.h>
  39
  40#define KPROBE_HASH_BITS 6
  41#define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)
  42
  43static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE];
  44
  45unsigned int kprobe_cpu = NR_CPUS;
  46static DEFINE_SPINLOCK(kprobe_lock);
  47
  48/* Locks kprobe: irqs must be disabled */
  49void lock_kprobes(void)
  50{
  51        spin_lock(&kprobe_lock);
  52        kprobe_cpu = smp_processor_id();
  53}
  54
  55void unlock_kprobes(void)
  56{
  57        kprobe_cpu = NR_CPUS;
  58        spin_unlock(&kprobe_lock);
  59}
  60
  61/* You have to be holding the kprobe_lock */
  62struct kprobe *get_kprobe(void *addr)
  63{
  64        struct hlist_head *head;
  65        struct hlist_node *node;
  66
  67        head = &kprobe_table[hash_ptr(addr, KPROBE_HASH_BITS)];
  68        hlist_for_each(node, head) {
  69                struct kprobe *p = hlist_entry(node, struct kprobe, hlist);
  70                if (p->addr == addr)
  71                        return p;
  72        }
  73        return NULL;
  74}
  75
  76int register_kprobe(struct kprobe *p)
  77{
  78        int ret = 0;
  79        unsigned long flags = 0;
  80
  81        if ((ret = arch_prepare_kprobe(p)) != 0) {
  82                goto out;
  83        }
  84        spin_lock_irqsave(&kprobe_lock, flags);
  85        INIT_HLIST_NODE(&p->hlist);
  86        if (get_kprobe(p->addr)) {
  87                ret = -EEXIST;
  88                goto out;
  89        }
  90        arch_copy_kprobe(p);
  91
  92        hlist_add_head(&p->hlist,
  93                       &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
  94
  95        p->opcode = *p->addr;
  96        *p->addr = BREAKPOINT_INSTRUCTION;
  97        flush_icache_range((unsigned long) p->addr,
  98                           (unsigned long) p->addr + sizeof(kprobe_opcode_t));
  99      out:
 100        spin_unlock_irqrestore(&kprobe_lock, flags);
 101        if (ret == -EEXIST)
 102                arch_remove_kprobe(p);
 103        return ret;
 104}
 105
 106void unregister_kprobe(struct kprobe *p)
 107{
 108        unsigned long flags;
 109        arch_remove_kprobe(p);
 110        spin_lock_irqsave(&kprobe_lock, flags);
 111        *p->addr = p->opcode;
 112        hlist_del(&p->hlist);
 113        flush_icache_range((unsigned long) p->addr,
 114                           (unsigned long) p->addr + sizeof(kprobe_opcode_t));
 115        spin_unlock_irqrestore(&kprobe_lock, flags);
 116}
 117
 118static struct notifier_block kprobe_exceptions_nb = {
 119        .notifier_call = kprobe_exceptions_notify,
 120        .priority = 0x7fffffff /* we need to notified first */
 121};
 122
 123int register_jprobe(struct jprobe *jp)
 124{
 125        /* Todo: Verify probepoint is a function entry point */
 126        jp->kp.pre_handler = setjmp_pre_handler;
 127        jp->kp.break_handler = longjmp_break_handler;
 128
 129        return register_kprobe(&jp->kp);
 130}
 131
 132void unregister_jprobe(struct jprobe *jp)
 133{
 134        unregister_kprobe(&jp->kp);
 135}
 136
 137static int __init init_kprobes(void)
 138{
 139        int i, err = 0;
 140
 141        /* FIXME allocate the probe table, currently defined statically */
 142        /* initialize all list heads */
 143        for (i = 0; i < KPROBE_TABLE_SIZE; i++)
 144                INIT_HLIST_HEAD(&kprobe_table[i]);
 145
 146        err = register_die_notifier(&kprobe_exceptions_nb);
 147        return err;
 148}
 149
 150__initcall(init_kprobes);
 151
 152EXPORT_SYMBOL_GPL(register_kprobe);
 153EXPORT_SYMBOL_GPL(unregister_kprobe);
 154EXPORT_SYMBOL_GPL(register_jprobe);
 155EXPORT_SYMBOL_GPL(unregister_jprobe);
 156EXPORT_SYMBOL_GPL(jprobe_return);
 157
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.