X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fpowerpc%2Fkernel%2Fkprobes.c;fp=arch%2Fpowerpc%2Fkernel%2Fkprobes.c;h=cfab48566db1306c3e8f47b5034ca703a67f471b;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=cd65c367b8b6d5c9abcc23fe7cbf595931814a5d;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index cd65c367b..cfab48566 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c @@ -1,5 +1,6 @@ /* * Kernel Probes (KProbes) + * arch/ppc64/kernel/kprobes.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,14 +27,13 @@ * for PPC64 */ +#include #include #include #include -#include #include #include #include -#include DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); @@ -61,8 +61,6 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) if (!ret) { memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t)); p->opcode = *p->addr; - flush_icache_range((unsigned long)p->ainsn.insn, - (unsigned long)p->ainsn.insn + sizeof(kprobe_opcode_t)); } return ret; @@ -84,39 +82,39 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p) { - mutex_lock(&kprobe_mutex); + down(&kprobe_mutex); free_insn_slot(p->ainsn.insn); - mutex_unlock(&kprobe_mutex); + up(&kprobe_mutex); } -static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs) { + kprobe_opcode_t insn = *p->ainsn.insn; + regs->msr |= MSR_SE; - /* - * On powerpc we should single step on the original - * instruction even if the probed insn is a trap - * variant as values in regs could play a part in - * if the trap is taken or not - */ - regs->nip = (unsigned long)p->ainsn.insn; + /* single step inline if it is a trap variant */ + if (is_trap(insn)) + regs->nip = (unsigned long)p->addr; + else + regs->nip = (unsigned long)p->ainsn.insn; } -static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) +static inline void save_previous_kprobe(struct kprobe_ctlblk *kcb) { kcb->prev_kprobe.kp = kprobe_running(); kcb->prev_kprobe.status = kcb->kprobe_status; kcb->prev_kprobe.saved_msr = kcb->kprobe_saved_msr; } -static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) +static inline void restore_previous_kprobe(struct kprobe_ctlblk *kcb) { __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp; kcb->kprobe_status = kcb->prev_kprobe.status; kcb->kprobe_saved_msr = kcb->prev_kprobe.saved_msr; } -static void __kprobes 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) { __get_cpu_var(current_kprobe) = p; @@ -142,7 +140,7 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe *rp, } } -static int __kprobes kprobe_handler(struct pt_regs *regs) +static inline int kprobe_handler(struct pt_regs *regs) { struct kprobe *p; int ret = 0; @@ -335,7 +333,7 @@ static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) regs->nip = (unsigned long)p->addr + 4; } -static int __kprobes post_kprobe_handler(struct pt_regs *regs) +static inline int post_kprobe_handler(struct pt_regs *regs) { struct kprobe *cur = kprobe_running(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); @@ -371,66 +369,21 @@ out: return 1; } -static int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) +static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) { struct kprobe *cur = kprobe_running(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); - const struct exception_table_entry *entry; - - switch(kcb->kprobe_status) { - case KPROBE_HIT_SS: - case KPROBE_REENTER: - /* - * We are here because the instruction being single - * stepped caused a page fault. We reset the current - * kprobe and the nip points back to the probe address - * and allow the page fault handler to continue as a - * normal page fault. - */ - regs->nip = (unsigned long)cur->addr; + + if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) + return 1; + + if (kcb->kprobe_status & KPROBE_HIT_SS) { + resume_execution(cur, regs); regs->msr &= ~MSR_SE; regs->msr |= kcb->kprobe_saved_msr; - if (kcb->kprobe_status == KPROBE_REENTER) - restore_previous_kprobe(kcb); - else - reset_current_kprobe(); - preempt_enable_no_resched(); - break; - case KPROBE_HIT_ACTIVE: - case KPROBE_HIT_SSDONE: - /* - * We increment the nmissed count for accounting, - * we can also use npre/npostfault count for accouting - * these specific fault cases. - */ - kprobes_inc_nmissed_count(cur); - - /* - * We come here because instructions in the pre/post - * handler caused the page_fault, this could happen - * if handler tries to access user space by - * copy_from_user(), get_user() etc. Let the - * user-specified handler try to fix it first. - */ - if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) - return 1; - /* - * In case the user-specified fault handler returned - * zero, try to fix up. - */ - if ((entry = search_exception_tables(regs->nip)) != NULL) { - regs->nip = entry->fixup; - return 1; - } - - /* - * fixup_exception() could not handle it, - * Let do_page_fault() fix it. - */ - break; - default: - break; + reset_current_kprobe(); + preempt_enable_no_resched(); } return 0; } @@ -444,9 +397,6 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, struct die_args *args = (struct die_args *)data; int ret = NOTIFY_DONE; - if (args->regs && user_mode(args->regs)) - return ret; - switch (val) { case DIE_BPT: if (kprobe_handler(args->regs))