Loading Documentation/features/debug/kprobes-on-ftrace/arch-support.txt +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ | parisc: | ok | | powerpc: | ok | | riscv: | TODO | | s390: | TODO | | s390: | ok | | sh: | TODO | | sparc: | TODO | | um: | TODO | Loading arch/s390/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -156,6 +156,7 @@ config S390 select HAVE_KERNEL_UNCOMPRESSED select HAVE_KERNEL_XZ select HAVE_KPROBES select HAVE_KPROBES_ON_FTRACE select HAVE_KRETPROBES select HAVE_KVM select HAVE_LIVEPATCH Loading arch/s390/include/asm/kprobes.h +0 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,6 @@ typedef u16 kprobe_opcode_t; struct arch_specific_insn { /* copy of original instruction */ kprobe_opcode_t *insn; unsigned int is_ftrace_insn : 1; }; struct prev_kprobe { Loading arch/s390/kernel/ftrace.c +46 −34 Original line number Diff line number Diff line Loading @@ -72,15 +72,6 @@ static inline void ftrace_generate_orig_insn(struct ftrace_insn *insn) #endif } static inline int is_kprobe_on_ftrace(struct ftrace_insn *insn) { #ifdef CONFIG_KPROBES if (insn->opc == BREAKPOINT_INSTRUCTION) return 1; #endif return 0; } static inline void ftrace_generate_kprobe_nop_insn(struct ftrace_insn *insn) { #ifdef CONFIG_KPROBES Loading Loading @@ -114,16 +105,6 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, /* Initial code replacement */ ftrace_generate_orig_insn(&orig); ftrace_generate_nop_insn(&new); } else if (is_kprobe_on_ftrace(&old)) { /* * If we find a breakpoint instruction, a kprobe has been * placed at the beginning of the function. We write the * constant KPROBE_ON_FTRACE_NOP into the remaining four * bytes of the original instruction so that the kprobes * handler can execute a nop, if it reaches this breakpoint. */ ftrace_generate_kprobe_call_insn(&orig); ftrace_generate_kprobe_nop_insn(&new); } else { /* Replace ftrace call with a nop. */ ftrace_generate_call_insn(&orig, rec->ip); Loading @@ -142,21 +123,10 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old))) return -EFAULT; if (is_kprobe_on_ftrace(&old)) { /* * If we find a breakpoint instruction, a kprobe has been * placed at the beginning of the function. We write the * constant KPROBE_ON_FTRACE_CALL into the remaining four * bytes of the original instruction so that the kprobes * handler can execute a brasl if it reaches this breakpoint. */ ftrace_generate_kprobe_nop_insn(&orig); ftrace_generate_kprobe_call_insn(&new); } else { /* Replace nop with an ftrace call. */ ftrace_generate_nop_insn(&orig); ftrace_generate_call_insn(&new, rec->ip); } /* Verify that the to be replaced code matches what we expect. */ if (memcmp(&orig, &old, sizeof(old))) return -EINVAL; Loading Loading @@ -241,3 +211,45 @@ int ftrace_disable_ftrace_graph_caller(void) } #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ #ifdef CONFIG_KPROBES_ON_FTRACE void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) { struct kprobe_ctlblk *kcb; struct kprobe *p = get_kprobe((kprobe_opcode_t *)ip); if (unlikely(!p) || kprobe_disabled(p)) return; if (kprobe_running()) { kprobes_inc_nmissed_count(p); return; } __this_cpu_write(current_kprobe, p); kcb = get_kprobe_ctlblk(); kcb->kprobe_status = KPROBE_HIT_ACTIVE; instruction_pointer_set(regs, ip); if (!p->pre_handler || !p->pre_handler(p, regs)) { instruction_pointer_set(regs, ip + MCOUNT_INSN_SIZE); if (unlikely(p->post_handler)) { kcb->kprobe_status = KPROBE_HIT_SSDONE; p->post_handler(p, regs, 0); } } __this_cpu_write(current_kprobe, NULL); } NOKPROBE_SYMBOL(kprobe_ftrace_handler); int arch_prepare_kprobe_ftrace(struct kprobe *p) { p->ainsn.insn = NULL; return 0; } #endif arch/s390/kernel/kprobes.c +5 −56 Original line number Diff line number Diff line Loading @@ -56,20 +56,9 @@ struct kprobe_insn_cache kprobe_s390_insn_slots = { static void copy_instruction(struct kprobe *p) { unsigned long ip = (unsigned long) p->addr; s64 disp, new_disp; u64 addr, new_addr; if (ftrace_location(ip) == ip) { /* * If kprobes patches the instruction that is morphed by * ftrace make sure that kprobes always sees the branch * "jg .+24" that skips the mcount block or the "brcl 0,0" * in case of hotpatch. */ ftrace_generate_nop_insn((struct ftrace_insn *)p->ainsn.insn); p->ainsn.is_ftrace_insn = 1; } else memcpy(p->ainsn.insn, p->addr, insn_length(*p->addr >> 8)); p->opcode = p->ainsn.insn[0]; if (!probe_is_insn_relative_long(p->ainsn.insn)) Loading Loading @@ -136,11 +125,6 @@ int arch_prepare_kprobe(struct kprobe *p) } NOKPROBE_SYMBOL(arch_prepare_kprobe); int arch_check_ftrace_location(struct kprobe *p) { return 0; } struct swap_insn_args { struct kprobe *p; unsigned int arm_kprobe : 1; Loading @@ -149,28 +133,11 @@ struct swap_insn_args { static int swap_instruction(void *data) { struct swap_insn_args *args = data; struct ftrace_insn new_insn, *insn; struct kprobe *p = args->p; size_t len; new_insn.opc = args->arm_kprobe ? BREAKPOINT_INSTRUCTION : p->opcode; len = sizeof(new_insn.opc); if (!p->ainsn.is_ftrace_insn) goto skip_ftrace; len = sizeof(new_insn); insn = (struct ftrace_insn *) p->addr; if (args->arm_kprobe) { if (is_ftrace_nop(insn)) new_insn.disp = KPROBE_ON_FTRACE_NOP; else new_insn.disp = KPROBE_ON_FTRACE_CALL; } else { ftrace_generate_call_insn(&new_insn, (unsigned long)p->addr); if (insn->disp == KPROBE_ON_FTRACE_NOP) ftrace_generate_nop_insn(&new_insn); } skip_ftrace: s390_kernel_write(p->addr, &new_insn, len); u16 opc; opc = args->arm_kprobe ? BREAKPOINT_INSTRUCTION : p->opcode; s390_kernel_write(p->addr, &opc, sizeof(opc)); return 0; } NOKPROBE_SYMBOL(swap_instruction); Loading Loading @@ -464,24 +431,6 @@ static void resume_execution(struct kprobe *p, struct pt_regs *regs) unsigned long ip = regs->psw.addr; int fixup = probe_get_fixup_type(p->ainsn.insn); /* Check if the kprobes location is an enabled ftrace caller */ if (p->ainsn.is_ftrace_insn) { struct ftrace_insn *insn = (struct ftrace_insn *) p->addr; struct ftrace_insn call_insn; ftrace_generate_call_insn(&call_insn, (unsigned long) p->addr); /* * A kprobe on an enabled ftrace call site actually single * stepped an unconditional branch (ftrace nop equivalent). * Now we need to fixup things and pretend that a brasl r0,... * was executed instead. */ if (insn->disp == KPROBE_ON_FTRACE_CALL) { ip += call_insn.disp * 2 - MCOUNT_INSN_SIZE; regs->gprs[0] = (unsigned long)p->addr + sizeof(*insn); } } if (fixup & FIXUP_PSW_NORMAL) ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn; Loading Loading
Documentation/features/debug/kprobes-on-ftrace/arch-support.txt +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ | parisc: | ok | | powerpc: | ok | | riscv: | TODO | | s390: | TODO | | s390: | ok | | sh: | TODO | | sparc: | TODO | | um: | TODO | Loading
arch/s390/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -156,6 +156,7 @@ config S390 select HAVE_KERNEL_UNCOMPRESSED select HAVE_KERNEL_XZ select HAVE_KPROBES select HAVE_KPROBES_ON_FTRACE select HAVE_KRETPROBES select HAVE_KVM select HAVE_LIVEPATCH Loading
arch/s390/include/asm/kprobes.h +0 −1 Original line number Diff line number Diff line Loading @@ -54,7 +54,6 @@ typedef u16 kprobe_opcode_t; struct arch_specific_insn { /* copy of original instruction */ kprobe_opcode_t *insn; unsigned int is_ftrace_insn : 1; }; struct prev_kprobe { Loading
arch/s390/kernel/ftrace.c +46 −34 Original line number Diff line number Diff line Loading @@ -72,15 +72,6 @@ static inline void ftrace_generate_orig_insn(struct ftrace_insn *insn) #endif } static inline int is_kprobe_on_ftrace(struct ftrace_insn *insn) { #ifdef CONFIG_KPROBES if (insn->opc == BREAKPOINT_INSTRUCTION) return 1; #endif return 0; } static inline void ftrace_generate_kprobe_nop_insn(struct ftrace_insn *insn) { #ifdef CONFIG_KPROBES Loading Loading @@ -114,16 +105,6 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, /* Initial code replacement */ ftrace_generate_orig_insn(&orig); ftrace_generate_nop_insn(&new); } else if (is_kprobe_on_ftrace(&old)) { /* * If we find a breakpoint instruction, a kprobe has been * placed at the beginning of the function. We write the * constant KPROBE_ON_FTRACE_NOP into the remaining four * bytes of the original instruction so that the kprobes * handler can execute a nop, if it reaches this breakpoint. */ ftrace_generate_kprobe_call_insn(&orig); ftrace_generate_kprobe_nop_insn(&new); } else { /* Replace ftrace call with a nop. */ ftrace_generate_call_insn(&orig, rec->ip); Loading @@ -142,21 +123,10 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old))) return -EFAULT; if (is_kprobe_on_ftrace(&old)) { /* * If we find a breakpoint instruction, a kprobe has been * placed at the beginning of the function. We write the * constant KPROBE_ON_FTRACE_CALL into the remaining four * bytes of the original instruction so that the kprobes * handler can execute a brasl if it reaches this breakpoint. */ ftrace_generate_kprobe_nop_insn(&orig); ftrace_generate_kprobe_call_insn(&new); } else { /* Replace nop with an ftrace call. */ ftrace_generate_nop_insn(&orig); ftrace_generate_call_insn(&new, rec->ip); } /* Verify that the to be replaced code matches what we expect. */ if (memcmp(&orig, &old, sizeof(old))) return -EINVAL; Loading Loading @@ -241,3 +211,45 @@ int ftrace_disable_ftrace_graph_caller(void) } #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ #ifdef CONFIG_KPROBES_ON_FTRACE void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *regs) { struct kprobe_ctlblk *kcb; struct kprobe *p = get_kprobe((kprobe_opcode_t *)ip); if (unlikely(!p) || kprobe_disabled(p)) return; if (kprobe_running()) { kprobes_inc_nmissed_count(p); return; } __this_cpu_write(current_kprobe, p); kcb = get_kprobe_ctlblk(); kcb->kprobe_status = KPROBE_HIT_ACTIVE; instruction_pointer_set(regs, ip); if (!p->pre_handler || !p->pre_handler(p, regs)) { instruction_pointer_set(regs, ip + MCOUNT_INSN_SIZE); if (unlikely(p->post_handler)) { kcb->kprobe_status = KPROBE_HIT_SSDONE; p->post_handler(p, regs, 0); } } __this_cpu_write(current_kprobe, NULL); } NOKPROBE_SYMBOL(kprobe_ftrace_handler); int arch_prepare_kprobe_ftrace(struct kprobe *p) { p->ainsn.insn = NULL; return 0; } #endif
arch/s390/kernel/kprobes.c +5 −56 Original line number Diff line number Diff line Loading @@ -56,20 +56,9 @@ struct kprobe_insn_cache kprobe_s390_insn_slots = { static void copy_instruction(struct kprobe *p) { unsigned long ip = (unsigned long) p->addr; s64 disp, new_disp; u64 addr, new_addr; if (ftrace_location(ip) == ip) { /* * If kprobes patches the instruction that is morphed by * ftrace make sure that kprobes always sees the branch * "jg .+24" that skips the mcount block or the "brcl 0,0" * in case of hotpatch. */ ftrace_generate_nop_insn((struct ftrace_insn *)p->ainsn.insn); p->ainsn.is_ftrace_insn = 1; } else memcpy(p->ainsn.insn, p->addr, insn_length(*p->addr >> 8)); p->opcode = p->ainsn.insn[0]; if (!probe_is_insn_relative_long(p->ainsn.insn)) Loading Loading @@ -136,11 +125,6 @@ int arch_prepare_kprobe(struct kprobe *p) } NOKPROBE_SYMBOL(arch_prepare_kprobe); int arch_check_ftrace_location(struct kprobe *p) { return 0; } struct swap_insn_args { struct kprobe *p; unsigned int arm_kprobe : 1; Loading @@ -149,28 +133,11 @@ struct swap_insn_args { static int swap_instruction(void *data) { struct swap_insn_args *args = data; struct ftrace_insn new_insn, *insn; struct kprobe *p = args->p; size_t len; new_insn.opc = args->arm_kprobe ? BREAKPOINT_INSTRUCTION : p->opcode; len = sizeof(new_insn.opc); if (!p->ainsn.is_ftrace_insn) goto skip_ftrace; len = sizeof(new_insn); insn = (struct ftrace_insn *) p->addr; if (args->arm_kprobe) { if (is_ftrace_nop(insn)) new_insn.disp = KPROBE_ON_FTRACE_NOP; else new_insn.disp = KPROBE_ON_FTRACE_CALL; } else { ftrace_generate_call_insn(&new_insn, (unsigned long)p->addr); if (insn->disp == KPROBE_ON_FTRACE_NOP) ftrace_generate_nop_insn(&new_insn); } skip_ftrace: s390_kernel_write(p->addr, &new_insn, len); u16 opc; opc = args->arm_kprobe ? BREAKPOINT_INSTRUCTION : p->opcode; s390_kernel_write(p->addr, &opc, sizeof(opc)); return 0; } NOKPROBE_SYMBOL(swap_instruction); Loading Loading @@ -464,24 +431,6 @@ static void resume_execution(struct kprobe *p, struct pt_regs *regs) unsigned long ip = regs->psw.addr; int fixup = probe_get_fixup_type(p->ainsn.insn); /* Check if the kprobes location is an enabled ftrace caller */ if (p->ainsn.is_ftrace_insn) { struct ftrace_insn *insn = (struct ftrace_insn *) p->addr; struct ftrace_insn call_insn; ftrace_generate_call_insn(&call_insn, (unsigned long) p->addr); /* * A kprobe on an enabled ftrace call site actually single * stepped an unconditional branch (ftrace nop equivalent). * Now we need to fixup things and pretend that a brasl r0,... * was executed instead. */ if (insn->disp == KPROBE_ON_FTRACE_CALL) { ip += call_insn.disp * 2 - MCOUNT_INSN_SIZE; regs->gprs[0] = (unsigned long)p->addr + sizeof(*insn); } } if (fixup & FIXUP_PSW_NORMAL) ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn; Loading