xref: /openbmc/linux/kernel/kprobes.c (revision 8b0914ea7475615c7c8965c1ac8fe4069270f25c)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  Kernel Probes (KProbes)
31da177e4SLinus Torvalds  *  kernel/kprobes.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
61da177e4SLinus Torvalds  * it under the terms of the GNU General Public License as published by
71da177e4SLinus Torvalds  * the Free Software Foundation; either version 2 of the License, or
81da177e4SLinus Torvalds  * (at your option) any later version.
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * This program is distributed in the hope that it will be useful,
111da177e4SLinus Torvalds  * but WITHOUT ANY WARRANTY; without even the implied warranty of
121da177e4SLinus Torvalds  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
131da177e4SLinus Torvalds  * GNU General Public License for more details.
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  * You should have received a copy of the GNU General Public License
161da177e4SLinus Torvalds  * along with this program; if not, write to the Free Software
171da177e4SLinus Torvalds  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
181da177e4SLinus Torvalds  *
191da177e4SLinus Torvalds  * Copyright (C) IBM Corporation, 2002, 2004
201da177e4SLinus Torvalds  *
211da177e4SLinus Torvalds  * 2002-Oct	Created by Vamsi Krishna S <vamsi_krishna@in.ibm.com> Kernel
221da177e4SLinus Torvalds  *		Probes initial implementation (includes suggestions from
231da177e4SLinus Torvalds  *		Rusty Russell).
241da177e4SLinus Torvalds  * 2004-Aug	Updated by Prasanna S Panchamukhi <prasanna@in.ibm.com> with
251da177e4SLinus Torvalds  *		hlists and exceptions notifier as suggested by Andi Kleen.
261da177e4SLinus Torvalds  * 2004-July	Suparna Bhattacharya <suparna@in.ibm.com> added jumper probes
271da177e4SLinus Torvalds  *		interface to access function arguments.
281da177e4SLinus Torvalds  * 2004-Sep	Prasanna S Panchamukhi <prasanna@in.ibm.com> Changed Kprobes
291da177e4SLinus Torvalds  *		exceptions notifier to be first on the priority list.
30b94cce92SHien Nguyen  * 2005-May	Hien Nguyen <hien@us.ibm.com>, Jim Keniston
31b94cce92SHien Nguyen  *		<jkenisto@us.ibm.com> and Prasanna S Panchamukhi
32b94cce92SHien Nguyen  *		<prasanna@in.ibm.com> added function-return probes.
331da177e4SLinus Torvalds  */
341da177e4SLinus Torvalds #include <linux/kprobes.h>
351da177e4SLinus Torvalds #include <linux/spinlock.h>
361da177e4SLinus Torvalds #include <linux/hash.h>
371da177e4SLinus Torvalds #include <linux/init.h>
381da177e4SLinus Torvalds #include <linux/module.h>
391da177e4SLinus Torvalds #include <asm/cacheflush.h>
401da177e4SLinus Torvalds #include <asm/errno.h>
411da177e4SLinus Torvalds #include <asm/kdebug.h>
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #define KPROBE_HASH_BITS 6
441da177e4SLinus Torvalds #define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE];
47b94cce92SHien Nguyen static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE];
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds unsigned int kprobe_cpu = NR_CPUS;
501da177e4SLinus Torvalds static DEFINE_SPINLOCK(kprobe_lock);
5164f562c6SAnanth N Mavinakayanahalli static struct kprobe *curr_kprobe;
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds /* Locks kprobe: irqs must be disabled */
541da177e4SLinus Torvalds void lock_kprobes(void)
551da177e4SLinus Torvalds {
561da177e4SLinus Torvalds 	spin_lock(&kprobe_lock);
571da177e4SLinus Torvalds 	kprobe_cpu = smp_processor_id();
581da177e4SLinus Torvalds }
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds void unlock_kprobes(void)
611da177e4SLinus Torvalds {
621da177e4SLinus Torvalds 	kprobe_cpu = NR_CPUS;
631da177e4SLinus Torvalds 	spin_unlock(&kprobe_lock);
641da177e4SLinus Torvalds }
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds /* You have to be holding the kprobe_lock */
671da177e4SLinus Torvalds struct kprobe *get_kprobe(void *addr)
681da177e4SLinus Torvalds {
691da177e4SLinus Torvalds 	struct hlist_head *head;
701da177e4SLinus Torvalds 	struct hlist_node *node;
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	head = &kprobe_table[hash_ptr(addr, KPROBE_HASH_BITS)];
731da177e4SLinus Torvalds 	hlist_for_each(node, head) {
741da177e4SLinus Torvalds 		struct kprobe *p = hlist_entry(node, struct kprobe, hlist);
751da177e4SLinus Torvalds 		if (p->addr == addr)
761da177e4SLinus Torvalds 			return p;
771da177e4SLinus Torvalds 	}
781da177e4SLinus Torvalds 	return NULL;
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds 
8164f562c6SAnanth N Mavinakayanahalli /*
8264f562c6SAnanth N Mavinakayanahalli  * Aggregate handlers for multiple kprobes support - these handlers
8364f562c6SAnanth N Mavinakayanahalli  * take care of invoking the individual kprobe handlers on p->list
8464f562c6SAnanth N Mavinakayanahalli  */
85b94cce92SHien Nguyen static int aggr_pre_handler(struct kprobe *p, struct pt_regs *regs)
8664f562c6SAnanth N Mavinakayanahalli {
8764f562c6SAnanth N Mavinakayanahalli 	struct kprobe *kp;
8864f562c6SAnanth N Mavinakayanahalli 
8964f562c6SAnanth N Mavinakayanahalli 	list_for_each_entry(kp, &p->list, list) {
9064f562c6SAnanth N Mavinakayanahalli 		if (kp->pre_handler) {
9164f562c6SAnanth N Mavinakayanahalli 			curr_kprobe = kp;
92*8b0914eaSPrasanna S Panchamukhi 			if (kp->pre_handler(kp, regs))
93*8b0914eaSPrasanna S Panchamukhi 				return 1;
9464f562c6SAnanth N Mavinakayanahalli 		}
95*8b0914eaSPrasanna S Panchamukhi 		curr_kprobe = NULL;
9664f562c6SAnanth N Mavinakayanahalli 	}
9764f562c6SAnanth N Mavinakayanahalli 	return 0;
9864f562c6SAnanth N Mavinakayanahalli }
9964f562c6SAnanth N Mavinakayanahalli 
100b94cce92SHien Nguyen static void aggr_post_handler(struct kprobe *p, struct pt_regs *regs,
10164f562c6SAnanth N Mavinakayanahalli 			      unsigned long flags)
10264f562c6SAnanth N Mavinakayanahalli {
10364f562c6SAnanth N Mavinakayanahalli 	struct kprobe *kp;
10464f562c6SAnanth N Mavinakayanahalli 
10564f562c6SAnanth N Mavinakayanahalli 	list_for_each_entry(kp, &p->list, list) {
10664f562c6SAnanth N Mavinakayanahalli 		if (kp->post_handler) {
10764f562c6SAnanth N Mavinakayanahalli 			curr_kprobe = kp;
10864f562c6SAnanth N Mavinakayanahalli 			kp->post_handler(kp, regs, flags);
10964f562c6SAnanth N Mavinakayanahalli 			curr_kprobe = NULL;
11064f562c6SAnanth N Mavinakayanahalli 		}
11164f562c6SAnanth N Mavinakayanahalli 	}
11264f562c6SAnanth N Mavinakayanahalli 	return;
11364f562c6SAnanth N Mavinakayanahalli }
11464f562c6SAnanth N Mavinakayanahalli 
115b94cce92SHien Nguyen static int aggr_fault_handler(struct kprobe *p, struct pt_regs *regs,
116b94cce92SHien Nguyen 			      int trapnr)
11764f562c6SAnanth N Mavinakayanahalli {
11864f562c6SAnanth N Mavinakayanahalli 	/*
11964f562c6SAnanth N Mavinakayanahalli 	 * if we faulted "during" the execution of a user specified
12064f562c6SAnanth N Mavinakayanahalli 	 * probe handler, invoke just that probe's fault handler
12164f562c6SAnanth N Mavinakayanahalli 	 */
12264f562c6SAnanth N Mavinakayanahalli 	if (curr_kprobe && curr_kprobe->fault_handler) {
12364f562c6SAnanth N Mavinakayanahalli 		if (curr_kprobe->fault_handler(curr_kprobe, regs, trapnr))
12464f562c6SAnanth N Mavinakayanahalli 			return 1;
12564f562c6SAnanth N Mavinakayanahalli 	}
12664f562c6SAnanth N Mavinakayanahalli 	return 0;
12764f562c6SAnanth N Mavinakayanahalli }
12864f562c6SAnanth N Mavinakayanahalli 
129*8b0914eaSPrasanna S Panchamukhi static int aggr_break_handler(struct kprobe *p, struct pt_regs *regs)
130*8b0914eaSPrasanna S Panchamukhi {
131*8b0914eaSPrasanna S Panchamukhi 	struct kprobe *kp = curr_kprobe;
132*8b0914eaSPrasanna S Panchamukhi 	if (curr_kprobe && kp->break_handler) {
133*8b0914eaSPrasanna S Panchamukhi 		if (kp->break_handler(kp, regs)) {
134*8b0914eaSPrasanna S Panchamukhi 			curr_kprobe = NULL;
135*8b0914eaSPrasanna S Panchamukhi 			return 1;
136*8b0914eaSPrasanna S Panchamukhi 		}
137*8b0914eaSPrasanna S Panchamukhi 	}
138*8b0914eaSPrasanna S Panchamukhi 	curr_kprobe = NULL;
139*8b0914eaSPrasanna S Panchamukhi 	return 0;
140*8b0914eaSPrasanna S Panchamukhi }
141*8b0914eaSPrasanna S Panchamukhi 
142b94cce92SHien Nguyen struct kprobe trampoline_p = {
143b94cce92SHien Nguyen 		.addr = (kprobe_opcode_t *) &kretprobe_trampoline,
144b94cce92SHien Nguyen 		.pre_handler = trampoline_probe_handler,
145b94cce92SHien Nguyen 		.post_handler = trampoline_post_handler
146b94cce92SHien Nguyen };
147b94cce92SHien Nguyen 
148b94cce92SHien Nguyen struct kretprobe_instance *get_free_rp_inst(struct kretprobe *rp)
149b94cce92SHien Nguyen {
150b94cce92SHien Nguyen 	struct hlist_node *node;
151b94cce92SHien Nguyen 	struct kretprobe_instance *ri;
152b94cce92SHien Nguyen 	hlist_for_each_entry(ri, node, &rp->free_instances, uflist)
153b94cce92SHien Nguyen 		return ri;
154b94cce92SHien Nguyen 	return NULL;
155b94cce92SHien Nguyen }
156b94cce92SHien Nguyen 
157b94cce92SHien Nguyen static struct kretprobe_instance *get_used_rp_inst(struct kretprobe *rp)
158b94cce92SHien Nguyen {
159b94cce92SHien Nguyen 	struct hlist_node *node;
160b94cce92SHien Nguyen 	struct kretprobe_instance *ri;
161b94cce92SHien Nguyen 	hlist_for_each_entry(ri, node, &rp->used_instances, uflist)
162b94cce92SHien Nguyen 		return ri;
163b94cce92SHien Nguyen 	return NULL;
164b94cce92SHien Nguyen }
165b94cce92SHien Nguyen 
166b94cce92SHien Nguyen struct kretprobe_instance *get_rp_inst(void *sara)
167b94cce92SHien Nguyen {
168b94cce92SHien Nguyen 	struct hlist_head *head;
169b94cce92SHien Nguyen 	struct hlist_node *node;
170b94cce92SHien Nguyen 	struct task_struct *tsk;
171b94cce92SHien Nguyen 	struct kretprobe_instance *ri;
172b94cce92SHien Nguyen 
173b94cce92SHien Nguyen 	tsk = arch_get_kprobe_task(sara);
174b94cce92SHien Nguyen 	head = &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)];
175b94cce92SHien Nguyen 	hlist_for_each_entry(ri, node, head, hlist) {
176b94cce92SHien Nguyen 		if (ri->stack_addr == sara)
177b94cce92SHien Nguyen 			return ri;
178b94cce92SHien Nguyen 	}
179b94cce92SHien Nguyen 	return NULL;
180b94cce92SHien Nguyen }
181b94cce92SHien Nguyen 
182b94cce92SHien Nguyen void add_rp_inst(struct kretprobe_instance *ri)
183b94cce92SHien Nguyen {
184b94cce92SHien Nguyen 	struct task_struct *tsk;
185b94cce92SHien Nguyen 	/*
186b94cce92SHien Nguyen 	 * Remove rp inst off the free list -
187b94cce92SHien Nguyen 	 * Add it back when probed function returns
188b94cce92SHien Nguyen 	 */
189b94cce92SHien Nguyen 	hlist_del(&ri->uflist);
190b94cce92SHien Nguyen 	tsk = arch_get_kprobe_task(ri->stack_addr);
191b94cce92SHien Nguyen 	/* Add rp inst onto table */
192b94cce92SHien Nguyen 	INIT_HLIST_NODE(&ri->hlist);
193b94cce92SHien Nguyen 	hlist_add_head(&ri->hlist,
194b94cce92SHien Nguyen 			&kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)]);
195b94cce92SHien Nguyen 
196b94cce92SHien Nguyen 	/* Also add this rp inst to the used list. */
197b94cce92SHien Nguyen 	INIT_HLIST_NODE(&ri->uflist);
198b94cce92SHien Nguyen 	hlist_add_head(&ri->uflist, &ri->rp->used_instances);
199b94cce92SHien Nguyen }
200b94cce92SHien Nguyen 
201b94cce92SHien Nguyen void recycle_rp_inst(struct kretprobe_instance *ri)
202b94cce92SHien Nguyen {
203b94cce92SHien Nguyen 	/* remove rp inst off the rprobe_inst_table */
204b94cce92SHien Nguyen 	hlist_del(&ri->hlist);
205b94cce92SHien Nguyen 	if (ri->rp) {
206b94cce92SHien Nguyen 		/* remove rp inst off the used list */
207b94cce92SHien Nguyen 		hlist_del(&ri->uflist);
208b94cce92SHien Nguyen 		/* put rp inst back onto the free list */
209b94cce92SHien Nguyen 		INIT_HLIST_NODE(&ri->uflist);
210b94cce92SHien Nguyen 		hlist_add_head(&ri->uflist, &ri->rp->free_instances);
211b94cce92SHien Nguyen 	} else
212b94cce92SHien Nguyen 		/* Unregistering */
213b94cce92SHien Nguyen 		kfree(ri);
214b94cce92SHien Nguyen }
215b94cce92SHien Nguyen 
216b94cce92SHien Nguyen struct hlist_head * kretprobe_inst_table_head(struct task_struct *tsk)
217b94cce92SHien Nguyen {
218b94cce92SHien Nguyen 	return &kretprobe_inst_table[hash_ptr(tsk, KPROBE_HASH_BITS)];
219b94cce92SHien Nguyen }
220b94cce92SHien Nguyen 
221b94cce92SHien Nguyen struct kretprobe_instance *get_rp_inst_tsk(struct task_struct *tk)
222b94cce92SHien Nguyen {
223b94cce92SHien Nguyen 	struct task_struct *tsk;
224b94cce92SHien Nguyen 	struct hlist_head *head;
225b94cce92SHien Nguyen 	struct hlist_node *node;
226b94cce92SHien Nguyen 	struct kretprobe_instance *ri;
227b94cce92SHien Nguyen 
228b94cce92SHien Nguyen 	head = &kretprobe_inst_table[hash_ptr(tk, KPROBE_HASH_BITS)];
229b94cce92SHien Nguyen 
230b94cce92SHien Nguyen 	hlist_for_each_entry(ri, node, head, hlist) {
231b94cce92SHien Nguyen 		tsk = arch_get_kprobe_task(ri->stack_addr);
232b94cce92SHien Nguyen 		if (tsk == tk)
233b94cce92SHien Nguyen 			return ri;
234b94cce92SHien Nguyen 	}
235b94cce92SHien Nguyen 	return NULL;
236b94cce92SHien Nguyen }
237b94cce92SHien Nguyen 
238b94cce92SHien Nguyen /*
239b94cce92SHien Nguyen  * This function is called from do_exit or do_execv when task tk's stack is
240b94cce92SHien Nguyen  * about to be recycled. Recycle any function-return probe instances
241b94cce92SHien Nguyen  * associated with this task. These represent probed functions that have
242b94cce92SHien Nguyen  * been called but may never return.
243b94cce92SHien Nguyen  */
244b94cce92SHien Nguyen void kprobe_flush_task(struct task_struct *tk)
245b94cce92SHien Nguyen {
2460aa55e4dSHien Nguyen 	unsigned long flags = 0;
2470aa55e4dSHien Nguyen 	spin_lock_irqsave(&kprobe_lock, flags);
2480aa55e4dSHien Nguyen 	arch_kprobe_flush_task(tk);
2490aa55e4dSHien Nguyen 	spin_unlock_irqrestore(&kprobe_lock, flags);
250b94cce92SHien Nguyen }
251b94cce92SHien Nguyen 
252b94cce92SHien Nguyen /*
253b94cce92SHien Nguyen  * This kprobe pre_handler is registered with every kretprobe. When probe
254b94cce92SHien Nguyen  * hits it will set up the return probe.
255b94cce92SHien Nguyen  */
256b94cce92SHien Nguyen static int pre_handler_kretprobe(struct kprobe *p, struct pt_regs *regs)
257b94cce92SHien Nguyen {
258b94cce92SHien Nguyen 	struct kretprobe *rp = container_of(p, struct kretprobe, kp);
259b94cce92SHien Nguyen 
260b94cce92SHien Nguyen 	/*TODO: consider to only swap the RA after the last pre_handler fired */
261b94cce92SHien Nguyen 	arch_prepare_kretprobe(rp, regs);
262b94cce92SHien Nguyen 	return 0;
263b94cce92SHien Nguyen }
264b94cce92SHien Nguyen 
265b94cce92SHien Nguyen static inline void free_rp_inst(struct kretprobe *rp)
266b94cce92SHien Nguyen {
267b94cce92SHien Nguyen 	struct kretprobe_instance *ri;
268b94cce92SHien Nguyen 	while ((ri = get_free_rp_inst(rp)) != NULL) {
269b94cce92SHien Nguyen 		hlist_del(&ri->uflist);
270b94cce92SHien Nguyen 		kfree(ri);
271b94cce92SHien Nguyen 	}
272b94cce92SHien Nguyen }
273b94cce92SHien Nguyen 
27464f562c6SAnanth N Mavinakayanahalli /*
275*8b0914eaSPrasanna S Panchamukhi  * Keep all fields in the kprobe consistent
276*8b0914eaSPrasanna S Panchamukhi  */
277*8b0914eaSPrasanna S Panchamukhi static inline void copy_kprobe(struct kprobe *old_p, struct kprobe *p)
278*8b0914eaSPrasanna S Panchamukhi {
279*8b0914eaSPrasanna S Panchamukhi 	memcpy(&p->opcode, &old_p->opcode, sizeof(kprobe_opcode_t));
280*8b0914eaSPrasanna S Panchamukhi 	memcpy(&p->ainsn, &old_p->ainsn, sizeof(struct arch_specific_insn));
281*8b0914eaSPrasanna S Panchamukhi }
282*8b0914eaSPrasanna S Panchamukhi 
283*8b0914eaSPrasanna S Panchamukhi /*
284*8b0914eaSPrasanna S Panchamukhi * Add the new probe to old_p->list. Fail if this is the
285*8b0914eaSPrasanna S Panchamukhi * second jprobe at the address - two jprobes can't coexist
286*8b0914eaSPrasanna S Panchamukhi */
287*8b0914eaSPrasanna S Panchamukhi static int add_new_kprobe(struct kprobe *old_p, struct kprobe *p)
288*8b0914eaSPrasanna S Panchamukhi {
289*8b0914eaSPrasanna S Panchamukhi         struct kprobe *kp;
290*8b0914eaSPrasanna S Panchamukhi 
291*8b0914eaSPrasanna S Panchamukhi 	if (p->break_handler) {
292*8b0914eaSPrasanna S Panchamukhi 		list_for_each_entry(kp, &old_p->list, list) {
293*8b0914eaSPrasanna S Panchamukhi 			if (kp->break_handler)
294*8b0914eaSPrasanna S Panchamukhi 				return -EEXIST;
295*8b0914eaSPrasanna S Panchamukhi 		}
296*8b0914eaSPrasanna S Panchamukhi 		list_add_tail(&p->list, &old_p->list);
297*8b0914eaSPrasanna S Panchamukhi 	} else
298*8b0914eaSPrasanna S Panchamukhi 		list_add(&p->list, &old_p->list);
299*8b0914eaSPrasanna S Panchamukhi 	return 0;
300*8b0914eaSPrasanna S Panchamukhi }
301*8b0914eaSPrasanna S Panchamukhi 
302*8b0914eaSPrasanna S Panchamukhi /*
30364f562c6SAnanth N Mavinakayanahalli  * Fill in the required fields of the "manager kprobe". Replace the
30464f562c6SAnanth N Mavinakayanahalli  * earlier kprobe in the hlist with the manager kprobe
30564f562c6SAnanth N Mavinakayanahalli  */
30664f562c6SAnanth N Mavinakayanahalli static inline void add_aggr_kprobe(struct kprobe *ap, struct kprobe *p)
30764f562c6SAnanth N Mavinakayanahalli {
308*8b0914eaSPrasanna S Panchamukhi 	copy_kprobe(p, ap);
30964f562c6SAnanth N Mavinakayanahalli 	ap->addr = p->addr;
31064f562c6SAnanth N Mavinakayanahalli 	ap->pre_handler = aggr_pre_handler;
31164f562c6SAnanth N Mavinakayanahalli 	ap->post_handler = aggr_post_handler;
31264f562c6SAnanth N Mavinakayanahalli 	ap->fault_handler = aggr_fault_handler;
313*8b0914eaSPrasanna S Panchamukhi 	ap->break_handler = aggr_break_handler;
31464f562c6SAnanth N Mavinakayanahalli 
31564f562c6SAnanth N Mavinakayanahalli 	INIT_LIST_HEAD(&ap->list);
31664f562c6SAnanth N Mavinakayanahalli 	list_add(&p->list, &ap->list);
31764f562c6SAnanth N Mavinakayanahalli 
31864f562c6SAnanth N Mavinakayanahalli 	INIT_HLIST_NODE(&ap->hlist);
31964f562c6SAnanth N Mavinakayanahalli 	hlist_del(&p->hlist);
32064f562c6SAnanth N Mavinakayanahalli 	hlist_add_head(&ap->hlist,
32164f562c6SAnanth N Mavinakayanahalli 		&kprobe_table[hash_ptr(ap->addr, KPROBE_HASH_BITS)]);
32264f562c6SAnanth N Mavinakayanahalli }
32364f562c6SAnanth N Mavinakayanahalli 
32464f562c6SAnanth N Mavinakayanahalli /*
32564f562c6SAnanth N Mavinakayanahalli  * This is the second or subsequent kprobe at the address - handle
32664f562c6SAnanth N Mavinakayanahalli  * the intricacies
32764f562c6SAnanth N Mavinakayanahalli  * TODO: Move kcalloc outside the spinlock
32864f562c6SAnanth N Mavinakayanahalli  */
32964f562c6SAnanth N Mavinakayanahalli static int register_aggr_kprobe(struct kprobe *old_p, struct kprobe *p)
33064f562c6SAnanth N Mavinakayanahalli {
33164f562c6SAnanth N Mavinakayanahalli 	int ret = 0;
33264f562c6SAnanth N Mavinakayanahalli 	struct kprobe *ap;
33364f562c6SAnanth N Mavinakayanahalli 
334*8b0914eaSPrasanna S Panchamukhi 	if (old_p->pre_handler == aggr_pre_handler) {
335*8b0914eaSPrasanna S Panchamukhi 		copy_kprobe(old_p, p);
336*8b0914eaSPrasanna S Panchamukhi 		ret = add_new_kprobe(old_p, p);
33764f562c6SAnanth N Mavinakayanahalli 	} else {
33864f562c6SAnanth N Mavinakayanahalli 		ap = kcalloc(1, sizeof(struct kprobe), GFP_ATOMIC);
33964f562c6SAnanth N Mavinakayanahalli 		if (!ap)
34064f562c6SAnanth N Mavinakayanahalli 			return -ENOMEM;
34164f562c6SAnanth N Mavinakayanahalli 		add_aggr_kprobe(ap, old_p);
342*8b0914eaSPrasanna S Panchamukhi 		copy_kprobe(ap, p);
343*8b0914eaSPrasanna S Panchamukhi 		ret = add_new_kprobe(ap, p);
34464f562c6SAnanth N Mavinakayanahalli 	}
34564f562c6SAnanth N Mavinakayanahalli 	return ret;
34664f562c6SAnanth N Mavinakayanahalli }
34764f562c6SAnanth N Mavinakayanahalli 
34864f562c6SAnanth N Mavinakayanahalli /* kprobe removal house-keeping routines */
34964f562c6SAnanth N Mavinakayanahalli static inline void cleanup_kprobe(struct kprobe *p, unsigned long flags)
35064f562c6SAnanth N Mavinakayanahalli {
3517e1048b1SRusty Lynch 	arch_disarm_kprobe(p);
35264f562c6SAnanth N Mavinakayanahalli 	hlist_del(&p->hlist);
35364f562c6SAnanth N Mavinakayanahalli 	spin_unlock_irqrestore(&kprobe_lock, flags);
35464f562c6SAnanth N Mavinakayanahalli 	arch_remove_kprobe(p);
35564f562c6SAnanth N Mavinakayanahalli }
35664f562c6SAnanth N Mavinakayanahalli 
35764f562c6SAnanth N Mavinakayanahalli static inline void cleanup_aggr_kprobe(struct kprobe *old_p,
35864f562c6SAnanth N Mavinakayanahalli 		struct kprobe *p, unsigned long flags)
35964f562c6SAnanth N Mavinakayanahalli {
36064f562c6SAnanth N Mavinakayanahalli 	list_del(&p->list);
36164f562c6SAnanth N Mavinakayanahalli 	if (list_empty(&old_p->list)) {
36264f562c6SAnanth N Mavinakayanahalli 		cleanup_kprobe(old_p, flags);
36364f562c6SAnanth N Mavinakayanahalli 		kfree(old_p);
36464f562c6SAnanth N Mavinakayanahalli 	} else
36564f562c6SAnanth N Mavinakayanahalli 		spin_unlock_irqrestore(&kprobe_lock, flags);
36664f562c6SAnanth N Mavinakayanahalli }
36764f562c6SAnanth N Mavinakayanahalli 
3681da177e4SLinus Torvalds int register_kprobe(struct kprobe *p)
3691da177e4SLinus Torvalds {
3701da177e4SLinus Torvalds 	int ret = 0;
3711da177e4SLinus Torvalds 	unsigned long flags = 0;
37264f562c6SAnanth N Mavinakayanahalli 	struct kprobe *old_p;
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds 	if ((ret = arch_prepare_kprobe(p)) != 0) {
3751da177e4SLinus Torvalds 		goto rm_kprobe;
3761da177e4SLinus Torvalds 	}
3771da177e4SLinus Torvalds 	spin_lock_irqsave(&kprobe_lock, flags);
37864f562c6SAnanth N Mavinakayanahalli 	old_p = get_kprobe(p->addr);
379ea32c65cSPrasanna S Panchamukhi 	p->nmissed = 0;
38064f562c6SAnanth N Mavinakayanahalli 	if (old_p) {
38164f562c6SAnanth N Mavinakayanahalli 		ret = register_aggr_kprobe(old_p, p);
3821da177e4SLinus Torvalds 		goto out;
3831da177e4SLinus Torvalds 	}
3841da177e4SLinus Torvalds 
38564f562c6SAnanth N Mavinakayanahalli 	arch_copy_kprobe(p);
38664f562c6SAnanth N Mavinakayanahalli 	INIT_HLIST_NODE(&p->hlist);
3871da177e4SLinus Torvalds 	hlist_add_head(&p->hlist,
3881da177e4SLinus Torvalds 		       &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
3891da177e4SLinus Torvalds 
3907e1048b1SRusty Lynch   	arch_arm_kprobe(p);
3917e1048b1SRusty Lynch 
3921da177e4SLinus Torvalds out:
3931da177e4SLinus Torvalds 	spin_unlock_irqrestore(&kprobe_lock, flags);
3941da177e4SLinus Torvalds rm_kprobe:
3951da177e4SLinus Torvalds 	if (ret == -EEXIST)
3961da177e4SLinus Torvalds 		arch_remove_kprobe(p);
3971da177e4SLinus Torvalds 	return ret;
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds 
4001da177e4SLinus Torvalds void unregister_kprobe(struct kprobe *p)
4011da177e4SLinus Torvalds {
4021da177e4SLinus Torvalds 	unsigned long flags;
40364f562c6SAnanth N Mavinakayanahalli 	struct kprobe *old_p;
40464f562c6SAnanth N Mavinakayanahalli 
4051da177e4SLinus Torvalds 	spin_lock_irqsave(&kprobe_lock, flags);
40664f562c6SAnanth N Mavinakayanahalli 	old_p = get_kprobe(p->addr);
40764f562c6SAnanth N Mavinakayanahalli 	if (old_p) {
40864f562c6SAnanth N Mavinakayanahalli 		if (old_p->pre_handler == aggr_pre_handler)
40964f562c6SAnanth N Mavinakayanahalli 			cleanup_aggr_kprobe(old_p, p, flags);
41064f562c6SAnanth N Mavinakayanahalli 		else
41164f562c6SAnanth N Mavinakayanahalli 			cleanup_kprobe(p, flags);
41264f562c6SAnanth N Mavinakayanahalli 	} else
41304dea5f9SPrasanna S Panchamukhi 		spin_unlock_irqrestore(&kprobe_lock, flags);
4141da177e4SLinus Torvalds }
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds static struct notifier_block kprobe_exceptions_nb = {
4171da177e4SLinus Torvalds 	.notifier_call = kprobe_exceptions_notify,
4181da177e4SLinus Torvalds 	.priority = 0x7fffffff /* we need to notified first */
4191da177e4SLinus Torvalds };
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds int register_jprobe(struct jprobe *jp)
4221da177e4SLinus Torvalds {
4231da177e4SLinus Torvalds 	/* Todo: Verify probepoint is a function entry point */
4241da177e4SLinus Torvalds 	jp->kp.pre_handler = setjmp_pre_handler;
4251da177e4SLinus Torvalds 	jp->kp.break_handler = longjmp_break_handler;
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	return register_kprobe(&jp->kp);
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds void unregister_jprobe(struct jprobe *jp)
4311da177e4SLinus Torvalds {
4321da177e4SLinus Torvalds 	unregister_kprobe(&jp->kp);
4331da177e4SLinus Torvalds }
4341da177e4SLinus Torvalds 
435b94cce92SHien Nguyen #ifdef ARCH_SUPPORTS_KRETPROBES
436b94cce92SHien Nguyen 
437b94cce92SHien Nguyen int register_kretprobe(struct kretprobe *rp)
438b94cce92SHien Nguyen {
439b94cce92SHien Nguyen 	int ret = 0;
440b94cce92SHien Nguyen 	struct kretprobe_instance *inst;
441b94cce92SHien Nguyen 	int i;
442b94cce92SHien Nguyen 
443b94cce92SHien Nguyen 	rp->kp.pre_handler = pre_handler_kretprobe;
444b94cce92SHien Nguyen 
445b94cce92SHien Nguyen 	/* Pre-allocate memory for max kretprobe instances */
446b94cce92SHien Nguyen 	if (rp->maxactive <= 0) {
447b94cce92SHien Nguyen #ifdef CONFIG_PREEMPT
448b94cce92SHien Nguyen 		rp->maxactive = max(10, 2 * NR_CPUS);
449b94cce92SHien Nguyen #else
450b94cce92SHien Nguyen 		rp->maxactive = NR_CPUS;
451b94cce92SHien Nguyen #endif
452b94cce92SHien Nguyen 	}
453b94cce92SHien Nguyen 	INIT_HLIST_HEAD(&rp->used_instances);
454b94cce92SHien Nguyen 	INIT_HLIST_HEAD(&rp->free_instances);
455b94cce92SHien Nguyen 	for (i = 0; i < rp->maxactive; i++) {
456b94cce92SHien Nguyen 		inst = kmalloc(sizeof(struct kretprobe_instance), GFP_KERNEL);
457b94cce92SHien Nguyen 		if (inst == NULL) {
458b94cce92SHien Nguyen 			free_rp_inst(rp);
459b94cce92SHien Nguyen 			return -ENOMEM;
460b94cce92SHien Nguyen 		}
461b94cce92SHien Nguyen 		INIT_HLIST_NODE(&inst->uflist);
462b94cce92SHien Nguyen 		hlist_add_head(&inst->uflist, &rp->free_instances);
463b94cce92SHien Nguyen 	}
464b94cce92SHien Nguyen 
465b94cce92SHien Nguyen 	rp->nmissed = 0;
466b94cce92SHien Nguyen 	/* Establish function entry probe point */
467b94cce92SHien Nguyen 	if ((ret = register_kprobe(&rp->kp)) != 0)
468b94cce92SHien Nguyen 		free_rp_inst(rp);
469b94cce92SHien Nguyen 	return ret;
470b94cce92SHien Nguyen }
471b94cce92SHien Nguyen 
472b94cce92SHien Nguyen #else /* ARCH_SUPPORTS_KRETPROBES */
473b94cce92SHien Nguyen 
474b94cce92SHien Nguyen int register_kretprobe(struct kretprobe *rp)
475b94cce92SHien Nguyen {
476b94cce92SHien Nguyen 	return -ENOSYS;
477b94cce92SHien Nguyen }
478b94cce92SHien Nguyen 
479b94cce92SHien Nguyen #endif /* ARCH_SUPPORTS_KRETPROBES */
480b94cce92SHien Nguyen 
481b94cce92SHien Nguyen void unregister_kretprobe(struct kretprobe *rp)
482b94cce92SHien Nguyen {
483b94cce92SHien Nguyen 	unsigned long flags;
484b94cce92SHien Nguyen 	struct kretprobe_instance *ri;
485b94cce92SHien Nguyen 
486b94cce92SHien Nguyen 	unregister_kprobe(&rp->kp);
487b94cce92SHien Nguyen 	/* No race here */
488b94cce92SHien Nguyen 	spin_lock_irqsave(&kprobe_lock, flags);
489b94cce92SHien Nguyen 	free_rp_inst(rp);
490b94cce92SHien Nguyen 	while ((ri = get_used_rp_inst(rp)) != NULL) {
491b94cce92SHien Nguyen 		ri->rp = NULL;
492b94cce92SHien Nguyen 		hlist_del(&ri->uflist);
493b94cce92SHien Nguyen 	}
494b94cce92SHien Nguyen 	spin_unlock_irqrestore(&kprobe_lock, flags);
495b94cce92SHien Nguyen }
496b94cce92SHien Nguyen 
4971da177e4SLinus Torvalds static int __init init_kprobes(void)
4981da177e4SLinus Torvalds {
4991da177e4SLinus Torvalds 	int i, err = 0;
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds 	/* FIXME allocate the probe table, currently defined statically */
5021da177e4SLinus Torvalds 	/* initialize all list heads */
503b94cce92SHien Nguyen 	for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
5041da177e4SLinus Torvalds 		INIT_HLIST_HEAD(&kprobe_table[i]);
505b94cce92SHien Nguyen 		INIT_HLIST_HEAD(&kretprobe_inst_table[i]);
506b94cce92SHien Nguyen 	}
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds 	err = register_die_notifier(&kprobe_exceptions_nb);
509b94cce92SHien Nguyen 	/* Register the trampoline probe for return probe */
510b94cce92SHien Nguyen 	register_kprobe(&trampoline_p);
5111da177e4SLinus Torvalds 	return err;
5121da177e4SLinus Torvalds }
5131da177e4SLinus Torvalds 
5141da177e4SLinus Torvalds __initcall(init_kprobes);
5151da177e4SLinus Torvalds 
5161da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(register_kprobe);
5171da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(unregister_kprobe);
5181da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(register_jprobe);
5191da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(unregister_jprobe);
5201da177e4SLinus Torvalds EXPORT_SYMBOL_GPL(jprobe_return);
521b94cce92SHien Nguyen EXPORT_SYMBOL_GPL(register_kretprobe);
522b94cce92SHien Nguyen EXPORT_SYMBOL_GPL(unregister_kretprobe);
523b94cce92SHien Nguyen 
524