Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Vineet Gupta | 1506 | 97.98% | 5 | 38.46% |
Masami Hiramatsu | 19 | 1.24% | 5 | 38.46% |
Christoph Lameter | 6 | 0.39% | 1 | 7.69% |
Josef Bacik | 4 | 0.26% | 1 | 7.69% |
Thomas Gleixner | 2 | 0.13% | 1 | 7.69% |
Total | 1537 | 13 |
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) */ #include <linux/types.h> #include <linux/kprobes.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/kdebug.h> #include <linux/sched.h> #include <linux/uaccess.h> #include <asm/cacheflush.h> #include <asm/current.h> #include <asm/disasm.h> #define MIN_STACK_SIZE(addr) min((unsigned long)MAX_STACK_SIZE, \ (unsigned long)current_thread_info() + THREAD_SIZE - (addr)) DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); int __kprobes arch_prepare_kprobe(struct kprobe *p) { /* Attempt to probe at unaligned address */ if ((unsigned long)p->addr & 0x01) return -EINVAL; /* Address should not be in exception handling code */ p->ainsn.is_short = is_short_instr((unsigned long)p->addr); p->opcode = *p->addr; return 0; } void __kprobes arch_arm_kprobe(struct kprobe *p) { *p->addr = UNIMP_S_INSTRUCTION; flush_icache_range((unsigned long)p->addr, (unsigned long)p->addr + sizeof(kprobe_opcode_t)); } void __kprobes arch_disarm_kprobe(struct kprobe *p) { *p->addr = p->opcode; flush_icache_range((unsigned long)p->addr, (unsigned long)p->addr + sizeof(kprobe_opcode_t)); } void __kprobes arch_remove_kprobe(struct kprobe *p) { arch_disarm_kprobe(p); /* Can we remove the kprobe in the middle of kprobe handling? */ if (p->ainsn.t1_addr) { *(p->ainsn.t1_addr) = p->ainsn.t1_opcode; flush_icache_range((unsigned long)p->ainsn.t1_addr, (unsigned long)p->ainsn.t1_addr + sizeof(kprobe_opcode_t)); p->ainsn.t1_addr = NULL; } if (p->ainsn.t2_addr) { *(p->ainsn.t2_addr) = p->ainsn.t2_opcode; flush_icache_range((unsigned long)p->ainsn.t2_addr, (unsigned long)p->ainsn.t2_addr + sizeof(kprobe_opcode_t)); p->ainsn.t2_addr = NULL; } } static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) { kcb->prev_kprobe.kp = kprobe_running(); kcb->prev_kprobe.status = kcb->kprobe_status; } static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) { __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); kcb->kprobe_status = kcb->prev_kprobe.status; } static inline void __kprobes set_current_kprobe(struct kprobe *p) { __this_cpu_write(current_kprobe, p); } static void __kprobes resume_execution(struct kprobe *p, unsigned long addr, struct pt_regs *regs) { /* Remove the trap instructions inserted for single step and * restore the original instructions */ if (p->ainsn.t1_addr) { *(p->ainsn.t1_addr) = p->ainsn.t1_opcode; flush_icache_range((unsigned long)p->ainsn.t1_addr, (unsigned long)p->ainsn.t1_addr + sizeof(kprobe_opcode_t)); p->ainsn.t1_addr = NULL; } if (p->ainsn.t2_addr) { *(p->ainsn.t2_addr) = p->ainsn.t2_opcode; flush_icache_range((unsigned long)p->ainsn.t2_addr, (unsigned long)p->ainsn.t2_addr + sizeof(kprobe_opcode_t)); p->ainsn.t2_addr = NULL; } return; } static void __kprobes setup_singlestep(struct kprobe *p, struct pt_regs *regs) { unsigned long next_pc; unsigned long tgt_if_br = 0; int is_branch; unsigned long bta; /* Copy the opcode back to the kprobe location and execute the * instruction. Because of this we will not be able to get into the * same kprobe until this kprobe is done */ *(p->addr) = p->opcode; flush_icache_range((unsigned long)p->addr, (unsigned long)p->addr + sizeof(kprobe_opcode_t)); /* Now we insert the trap at the next location after this instruction to * single step. If it is a branch we insert the trap at possible branch * targets */ bta = regs->bta; if (regs->status32 & 0x40) { /* We are in a delay slot with the branch taken */ next_pc = bta & ~0x01; if (!p->ainsn.is_short) { if (bta & 0x01) regs->blink += 2; else { /* Branch not taken */ next_pc += 2; /* next pc is taken from bta after executing the * delay slot instruction */ regs->bta += 2; } } is_branch = 0; } else is_branch = disasm_next_pc((unsigned long)p->addr, regs, (struct callee_regs *) current->thread.callee_reg, &next_pc, &tgt_if_br); p->ainsn.t1_addr = (kprobe_opcode_t *) next_pc; p->ainsn.t1_opcode = *(p->ainsn.t1_addr); *(p->ainsn.t1_addr) = TRAP_S_2_INSTRUCTION; flush_icache_range((unsigned long)p->ainsn.t1_addr, (unsigned long)p->ainsn.t1_addr + sizeof(kprobe_opcode_t)); if (is_branch) { p->ainsn.t2_addr = (kprobe_opcode_t *) tgt_if_br; p->ainsn.t2_opcode = *(p->ainsn.t2_addr); *(p->ainsn.t2_addr) = TRAP_S_2_INSTRUCTION; flush_icache_range((unsigned long)p->ainsn.t2_addr, (unsigned long)p->ainsn.t2_addr + sizeof(kprobe_opcode_t)); } } static int __kprobes arc_kprobe_handler(unsigned long addr, struct pt_regs *regs) { struct kprobe *p; struct kprobe_ctlblk *kcb; preempt_disable(); kcb = get_kprobe_ctlblk(); p = get_kprobe((unsigned long *)addr); if (p) { /* * We have reentered the kprobe_handler, since another kprobe * was hit while within the handler, we save the original * kprobes and single step on the instruction of the new probe * without calling any user handlers to avoid recursive * kprobes. */ if (kprobe_running()) { save_previous_kprobe(kcb); set_current_kprobe(p); kprobes_inc_nmissed_count(p); setup_singlestep(p, regs); kcb->kprobe_status = KPROBE_REENTER; return 1; } set_current_kprobe(p); kcb->kprobe_status = KPROBE_HIT_ACTIVE; /* If we have no pre-handler or it returned 0, we continue with * normal processing. If we have a pre-handler and it returned * non-zero - which means user handler setup registers to exit * to another instruction, we must skip the single stepping. */ if (!p->pre_handler || !p->pre_handler(p, regs)) { setup_singlestep(p, regs); kcb->kprobe_status = KPROBE_HIT_SS; } else { reset_current_kprobe(); preempt_enable_no_resched(); } return 1; } /* no_kprobe: */ preempt_enable_no_resched(); return 0; } static int __kprobes arc_post_kprobe_handler(unsigned long addr, struct pt_regs *regs) { struct kprobe *cur = kprobe_running(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); if (!cur) return 0; resume_execution(cur, addr, regs); /* Rearm the kprobe */ arch_arm_kprobe(cur); /* * When we return from trap instruction we go to the next instruction * We restored the actual instruction in resume_exectuiont and we to * return to the same address and execute it */ regs->ret = addr; if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) { kcb->kprobe_status = KPROBE_HIT_SSDONE; cur->post_handler(cur, regs, 0); } if (kcb->kprobe_status == KPROBE_REENTER) { restore_previous_kprobe(kcb); goto out; } reset_current_kprobe(); out: preempt_enable_no_resched(); return 1; } /* * Fault can be for the instruction being single stepped or for the * pre/post handlers in the module. * This is applicable for applications like user probes, where we have the * probe in user space and the handlers in the kernel */ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned long trapnr) { struct kprobe *cur = kprobe_running(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); switch (kcb->kprobe_status) { case KPROBE_HIT_SS: case KPROBE_REENTER: /* * We are here because the instruction being single stepped * caused the fault. We reset the current kprobe and allow the * exception handler as if it is regular exception. In our * case it doesn't matter because the system will be halted */ resume_execution(cur, (unsigned long)cur->addr, regs); 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 are here because the instructions in the pre/post handler * caused the fault. */ /* * In case the user-specified fault handler returned zero, * try to fix up. */ if (fixup_exception(regs)) return 1; /* * fixup_exception() could not handle it, * Let do_page_fault() fix it. */ break; default: break; } return 0; } int __kprobes kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *data) { struct die_args *args = data; unsigned long addr = args->err; int ret = NOTIFY_DONE; switch (val) { case DIE_IERR: if (arc_kprobe_handler(addr, args->regs)) return NOTIFY_STOP; break; case DIE_TRAP: if (arc_post_kprobe_handler(addr, args->regs)) return NOTIFY_STOP; break; default: break; } return ret; } static void __used kretprobe_trampoline_holder(void) { __asm__ __volatile__(".global __kretprobe_trampoline\n" "__kretprobe_trampoline:\n" "nop\n"); } void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { ri->ret_addr = (kprobe_opcode_t *) regs->blink; ri->fp = NULL; /* Replace the return addr with trampoline addr */ regs->blink = (unsigned long)&__kretprobe_trampoline; } static int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) { regs->ret = __kretprobe_trampoline_handler(regs, NULL); /* By returning a non zero value, we are telling the kprobe handler * that we don't want the post_handler to run */ return 1; } static struct kprobe trampoline_p = { .addr = (kprobe_opcode_t *) &__kretprobe_trampoline, .pre_handler = trampoline_probe_handler }; int __init arch_init_kprobes(void) { /* Registering the trampoline code for the kret probe */ return register_kprobe(&trampoline_p); } int __kprobes arch_trampoline_kprobe(struct kprobe *p) { if (p->addr == (kprobe_opcode_t *) &__kretprobe_trampoline) return 1; return 0; } void trap_is_kprobe(unsigned long address, struct pt_regs *regs) { notify_die(DIE_TRAP, "kprobe_trap", regs, address, 0, SIGTRAP); }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1