Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f215d985 authored by Ananth N Mavinakayanahalli's avatar Ananth N Mavinakayanahalli Committed by Linus Torvalds
Browse files

[PATCH] Kprobes: Track kprobe on a per_cpu basis - sparc64 changes



Sparc64 changes to track kprobe execution on a per-cpu basis.  We now track
the kprobe state machine independently on each cpu using an arch specific
kprobe control block.

Signed-off-by: default avatarAnanth N Mavinakayanahalli <ananth@in.ibm.com>
Signed-off-by: default avatarAnil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Cc: "David S. Miller" <davem@davemloft.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 0dc036c9
Loading
Loading
Loading
Loading
+67 −64
Original line number Original line Diff line number Diff line
@@ -38,6 +38,9 @@
 * - Mark that we are no longer actively in a kprobe.
 * - Mark that we are no longer actively in a kprobe.
 */
 */


DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);

int __kprobes arch_prepare_kprobe(struct kprobe *p)
int __kprobes arch_prepare_kprobe(struct kprobe *p)
{
{
	return 0;
	return 0;
@@ -66,46 +69,39 @@ void __kprobes arch_remove_kprobe(struct kprobe *p)
{
{
}
}


static struct kprobe *current_kprobe;
static inline void save_previous_kprobe(struct kprobe_ctlblk *kcb)
static unsigned long current_kprobe_orig_tnpc;
static unsigned long current_kprobe_orig_tstate_pil;
static unsigned int kprobe_status;
static struct kprobe *kprobe_prev;
static unsigned long kprobe_orig_tnpc_prev;
static unsigned long kprobe_orig_tstate_pil_prev;
static unsigned int kprobe_status_prev;

static inline void save_previous_kprobe(void)
{
{
	kprobe_status_prev = kprobe_status;
	kcb->prev_kprobe.kp = kprobe_running();
	kprobe_orig_tnpc_prev = current_kprobe_orig_tnpc;
	kcb->prev_kprobe.status = kcb->kprobe_status;
	kprobe_orig_tstate_pil_prev = current_kprobe_orig_tstate_pil;
	kcb->prev_kprobe.orig_tnpc = kcb->kprobe_orig_tnpc;
	kprobe_prev = current_kprobe;
	kcb->prev_kprobe.orig_tstate_pil = kcb->kprobe_orig_tstate_pil;
}
}


static inline void restore_previous_kprobe(void)
static inline void restore_previous_kprobe(struct kprobe_ctlblk *kcb)
{
{
	kprobe_status = kprobe_status_prev;
	__get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
	current_kprobe_orig_tnpc = kprobe_orig_tnpc_prev;
	kcb->kprobe_status = kcb->prev_kprobe.status;
	current_kprobe_orig_tstate_pil = kprobe_orig_tstate_pil_prev;
	kcb->kprobe_orig_tnpc = kcb->prev_kprobe.orig_tnpc;
	current_kprobe = kprobe_prev;
	kcb->kprobe_orig_tstate_pil = kcb->prev_kprobe.orig_tstate_pil;
}
}


static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs)
static inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
				struct kprobe_ctlblk *kcb)
{
{
	current_kprobe_orig_tnpc = regs->tnpc;
	__get_cpu_var(current_kprobe) = p;
	current_kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL);
	kcb->kprobe_orig_tnpc = regs->tnpc;
	current_kprobe = p;
	kcb->kprobe_orig_tstate_pil = (regs->tstate & TSTATE_PIL);
}
}


static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs,
			struct kprobe_ctlblk *kcb)
{
{
	regs->tstate |= TSTATE_PIL;
	regs->tstate |= TSTATE_PIL;


	/*single step inline, if it a breakpoint instruction*/
	/*single step inline, if it a breakpoint instruction*/
	if (p->opcode == BREAKPOINT_INSTRUCTION) {
	if (p->opcode == BREAKPOINT_INSTRUCTION) {
		regs->tpc = (unsigned long) p->addr;
		regs->tpc = (unsigned long) p->addr;
		regs->tnpc = current_kprobe_orig_tnpc;
		regs->tnpc = kcb->kprobe_orig_tnpc;
	} else {
	} else {
		regs->tpc = (unsigned long) &p->ainsn.insn[0];
		regs->tpc = (unsigned long) &p->ainsn.insn[0];
		regs->tnpc = (unsigned long) &p->ainsn.insn[1];
		regs->tnpc = (unsigned long) &p->ainsn.insn[1];
@@ -117,6 +113,7 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
	struct kprobe *p;
	struct kprobe *p;
	void *addr = (void *) regs->tpc;
	void *addr = (void *) regs->tpc;
	int ret = 0;
	int ret = 0;
	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();


	if (kprobe_running()) {
	if (kprobe_running()) {
		/* We *are* holding lock here, so this is safe.
		/* We *are* holding lock here, so this is safe.
@@ -124,9 +121,9 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
		 */
		 */
		p = get_kprobe(addr);
		p = get_kprobe(addr);
		if (p) {
		if (p) {
			if (kprobe_status == KPROBE_HIT_SS) {
			if (kcb->kprobe_status == KPROBE_HIT_SS) {
				regs->tstate = ((regs->tstate & ~TSTATE_PIL) |
				regs->tstate = ((regs->tstate & ~TSTATE_PIL) |
					current_kprobe_orig_tstate_pil);
					kcb->kprobe_orig_tstate_pil);
				unlock_kprobes();
				unlock_kprobes();
				goto no_kprobe;
				goto no_kprobe;
			}
			}
@@ -136,14 +133,14 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
			 * just single step on the instruction of the new probe
			 * just single step on the instruction of the new probe
			 * without calling any user handlers.
			 * without calling any user handlers.
			 */
			 */
			save_previous_kprobe();
			save_previous_kprobe(kcb);
			set_current_kprobe(p, regs);
			set_current_kprobe(p, regs, kcb);
			p->nmissed++;
			p->nmissed++;
			kprobe_status = KPROBE_REENTER;
			kcb->kprobe_status = KPROBE_REENTER;
			prepare_singlestep(p, regs);
			prepare_singlestep(p, regs, kcb);
			return 1;
			return 1;
		} else {
		} else {
			p = current_kprobe;
			p = __get_cpu_var(current_kprobe);
			if (p->break_handler && p->break_handler(p, regs))
			if (p->break_handler && p->break_handler(p, regs))
				goto ss_probe;
				goto ss_probe;
		}
		}
@@ -174,14 +171,14 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
	 * in post_kprobes_handler()
	 * in post_kprobes_handler()
	 */
	 */
	preempt_disable();
	preempt_disable();
	set_current_kprobe(p, regs);
	set_current_kprobe(p, regs, kcb);
	kprobe_status = KPROBE_HIT_ACTIVE;
	kcb->kprobe_status = KPROBE_HIT_ACTIVE;
	if (p->pre_handler && p->pre_handler(p, regs))
	if (p->pre_handler && p->pre_handler(p, regs))
		return 1;
		return 1;


ss_probe:
ss_probe:
	prepare_singlestep(p, regs);
	prepare_singlestep(p, regs, kcb);
	kprobe_status = KPROBE_HIT_SS;
	kcb->kprobe_status = KPROBE_HIT_SS;
	return 1;
	return 1;


no_kprobe:
no_kprobe:
@@ -262,11 +259,12 @@ static void __kprobes retpc_fixup(struct pt_regs *regs, u32 insn,
 * This function prepares to return from the post-single-step
 * This function prepares to return from the post-single-step
 * breakpoint trap.
 * breakpoint trap.
 */
 */
static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
static void __kprobes resume_execution(struct kprobe *p,
		struct pt_regs *regs, struct kprobe_ctlblk *kcb)
{
{
	u32 insn = p->ainsn.insn[0];
	u32 insn = p->ainsn.insn[0];


	regs->tpc = current_kprobe_orig_tnpc;
	regs->tpc = kcb->kprobe_orig_tnpc;
	regs->tnpc = relbranch_fixup(insn,
	regs->tnpc = relbranch_fixup(insn,
				     (unsigned long) p->addr,
				     (unsigned long) p->addr,
				     (unsigned long) &p->ainsn.insn[0],
				     (unsigned long) &p->ainsn.insn[0],
@@ -274,26 +272,30 @@ static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
	retpc_fixup(regs, insn, (unsigned long) p->addr);
	retpc_fixup(regs, insn, (unsigned long) p->addr);


	regs->tstate = ((regs->tstate & ~TSTATE_PIL) |
	regs->tstate = ((regs->tstate & ~TSTATE_PIL) |
			current_kprobe_orig_tstate_pil);
			kcb->kprobe_orig_tstate_pil);
}
}


static inline int post_kprobe_handler(struct pt_regs *regs)
static inline int post_kprobe_handler(struct pt_regs *regs)
{
{
	if (!kprobe_running())
	struct kprobe *cur = kprobe_running();
	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();

	if (!cur)
		return 0;
		return 0;


	if ((kprobe_status != KPROBE_REENTER) && current_kprobe->post_handler) {
	if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {
		kprobe_status = KPROBE_HIT_SSDONE;
		kcb->kprobe_status = KPROBE_HIT_SSDONE;
		current_kprobe->post_handler(current_kprobe, regs, 0);
		cur->post_handler(cur, regs, 0);
	}
	}


	resume_execution(current_kprobe, regs);
	resume_execution(cur, regs, kcb);


	/*Restore back the original saved kprobes variables and continue. */
	/*Restore back the original saved kprobes variables and continue. */
	if (kprobe_status == KPROBE_REENTER) {
	if (kcb->kprobe_status == KPROBE_REENTER) {
		restore_previous_kprobe();
		restore_previous_kprobe(kcb);
		goto out;
		goto out;
	}
	}
	reset_current_kprobe();
	unlock_kprobes();
	unlock_kprobes();
out:
out:
	preempt_enable_no_resched();
	preempt_enable_no_resched();
@@ -304,13 +306,16 @@ static inline int post_kprobe_handler(struct pt_regs *regs)
/* Interrupts disabled, kprobe_lock held. */
/* Interrupts disabled, kprobe_lock held. */
static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
{
{
	if (current_kprobe->fault_handler
	struct kprobe *cur = kprobe_running();
	    && current_kprobe->fault_handler(current_kprobe, regs, trapnr))
	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();

	if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
		return 1;
		return 1;


	if (kprobe_status & KPROBE_HIT_SS) {
	if (kcb->kprobe_status & KPROBE_HIT_SS) {
		resume_execution(current_kprobe, regs);
		resume_execution(cur, regs, kcb);


		reset_current_kprobe();
		unlock_kprobes();
		unlock_kprobes();
		preempt_enable_no_resched();
		preempt_enable_no_resched();
	}
	}
@@ -370,24 +375,21 @@ asmlinkage void __kprobes kprobe_trap(unsigned long trap_level,
}
}


/* Jprobes support.  */
/* Jprobes support.  */
static struct pt_regs jprobe_saved_regs;
static struct pt_regs *jprobe_saved_regs_location;
static struct sparc_stackf jprobe_saved_stack;

int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
{
{
	struct jprobe *jp = container_of(p, struct jprobe, kp);
	struct jprobe *jp = container_of(p, struct jprobe, kp);
	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();


	jprobe_saved_regs_location = regs;
	kcb->jprobe_saved_regs_location = regs;
	memcpy(&jprobe_saved_regs, regs, sizeof(*regs));
	memcpy(&(kcb->jprobe_saved_regs), regs, sizeof(*regs));


	/* Save a whole stack frame, this gets arguments
	/* Save a whole stack frame, this gets arguments
	 * pushed onto the stack after using up all the
	 * pushed onto the stack after using up all the
	 * arg registers.
	 * arg registers.
	 */
	 */
	memcpy(&jprobe_saved_stack,
	memcpy(&(kcb->jprobe_saved_stack),
	       (char *) (regs->u_regs[UREG_FP] + STACK_BIAS),
	       (char *) (regs->u_regs[UREG_FP] + STACK_BIAS),
	       sizeof(jprobe_saved_stack));
	       sizeof(kcb->jprobe_saved_stack));


	regs->tpc  = (unsigned long) jp->entry;
	regs->tpc  = (unsigned long) jp->entry;
	regs->tnpc = ((unsigned long) jp->entry) + 0x4UL;
	regs->tnpc = ((unsigned long) jp->entry) + 0x4UL;
@@ -411,14 +413,15 @@ extern void __show_regs(struct pt_regs * regs);
int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
{
{
	u32 *addr = (u32 *) regs->tpc;
	u32 *addr = (u32 *) regs->tpc;
	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();


	if (addr == (u32 *) jprobe_return_trap_instruction) {
	if (addr == (u32 *) jprobe_return_trap_instruction) {
		if (jprobe_saved_regs_location != regs) {
		if (kcb->jprobe_saved_regs_location != regs) {
			printk("JPROBE: Current regs (%p) does not match "
			printk("JPROBE: Current regs (%p) does not match "
			       "saved regs (%p).\n",
			       "saved regs (%p).\n",
			       regs, jprobe_saved_regs_location);
			       regs, kcb->jprobe_saved_regs_location);
			printk("JPROBE: Saved registers\n");
			printk("JPROBE: Saved registers\n");
			__show_regs(jprobe_saved_regs_location);
			__show_regs(kcb->jprobe_saved_regs_location);
			printk("JPROBE: Current registers\n");
			printk("JPROBE: Current registers\n");
			__show_regs(regs);
			__show_regs(regs);
			BUG();
			BUG();
@@ -427,11 +430,11 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
		 * first so that UREG_FP is the original one for
		 * first so that UREG_FP is the original one for
		 * the stack frame restore.
		 * the stack frame restore.
		 */
		 */
		memcpy(regs, &jprobe_saved_regs, sizeof(*regs));
		memcpy(regs, &(kcb->jprobe_saved_regs), sizeof(*regs));


		memcpy((char *) (regs->u_regs[UREG_FP] + STACK_BIAS),
		memcpy((char *) (regs->u_regs[UREG_FP] + STACK_BIAS),
		       &jprobe_saved_stack,
		       &(kcb->jprobe_saved_stack),
		       sizeof(jprobe_saved_stack));
		       sizeof(kcb->jprobe_saved_stack));


		return 1;
		return 1;
	}
	}
+20 −0
Original line number Original line Diff line number Diff line
@@ -3,6 +3,7 @@


#include <linux/config.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/types.h>
#include <linux/percpu.h>


typedef u32 kprobe_opcode_t;
typedef u32 kprobe_opcode_t;


@@ -18,6 +19,25 @@ struct arch_specific_insn {
	kprobe_opcode_t insn[MAX_INSN_SIZE];
	kprobe_opcode_t insn[MAX_INSN_SIZE];
};
};


struct prev_kprobe {
	struct kprobe *kp;
	unsigned int status;
	unsigned long orig_tnpc;
	unsigned long orig_tstate_pil;
};

/* per-cpu kprobe control block */
struct kprobe_ctlblk {
	unsigned long kprobe_status;
	unsigned long kprobe_orig_tnpc;
	unsigned long kprobe_orig_tstate_pil;
	long *jprobe_saved_esp;
	struct pt_regs jprobe_saved_regs;
	struct pt_regs *jprobe_saved_regs_location;
	struct sparc_stackf jprobe_saved_stack;
	struct prev_kprobe prev_kprobe;
};

#ifdef CONFIG_KPROBES
#ifdef CONFIG_KPROBES
extern int kprobe_exceptions_notify(struct notifier_block *self,
extern int kprobe_exceptions_notify(struct notifier_block *self,
				    unsigned long val, void *data);
				    unsigned long val, void *data);