From nobody Thu Apr 2 23:02:05 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C07FAC54EE9 for ; Tue, 20 Sep 2022 03:37:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229909AbiITDhW (ORCPT ); Mon, 19 Sep 2022 23:37:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47992 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229645AbiITDhS (ORCPT ); Mon, 19 Sep 2022 23:37:18 -0400 Received: from loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id BE5505A147 for ; Mon, 19 Sep 2022 20:37:16 -0700 (PDT) Received: from linux.localdomain (unknown [113.200.148.30]) by localhost.localdomain (Coremail) with SMTP id AQAAf8DxReJqNSlj7FseAA--.48978S3; Tue, 20 Sep 2022 11:37:15 +0800 (CST) From: Tiezhu Yang To: Huacai Chen , Masami Hiramatsu Cc: loongarch@lists.linux.dev, linux-kernel@vger.kernel.org Subject: [PATCH 1/4] LoongArch: Add kprobe support Date: Tue, 20 Sep 2022 11:37:11 +0800 Message-Id: <1663645034-967-2-git-send-email-yangtiezhu@loongson.cn> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1663645034-967-1-git-send-email-yangtiezhu@loongson.cn> References: <1663645034-967-1-git-send-email-yangtiezhu@loongson.cn> X-CM-TRANSID: AQAAf8DxReJqNSlj7FseAA--.48978S3 X-Coremail-Antispam: 1UD129KBjvAXoW3KryUtF43CF13GrWrGrW5trb_yoW8ArW8Zo WSvF4qgr48KrW5WF4rJr1qqFyUW3W8WFWrAFWavFs8uF17Ar15Xr4jkrW8Ja4aqF4Fg3yf C39xua4fAayxuFn3n29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UjIYCTnIWjp_UUUYX7k0a2IF6w4kM7kC6x804xWl14x267AKxVW8JVW5JwAFc2x0 x2IEx4CE42xK8VAvwI8IcIk0rVWrJVCq3wAFIxvE14AKwVWUJVWUGwA2048vs2IY020E87 I2jVAFwI0_Jr4l82xGYIkIc2x26xkF7I0E14v26r1Y6r1xM28lY4IEw2IIxxk0rwA2F7IY 1VAKz4vEj48ve4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_Xr0_Ar1l84ACjcxK6xIIjxv20x vEc7CjxVAFwI0_Gr0_Cr1l84ACjcxK6I8E87Iv67AKxVW8Jr0_Cr1UM28EF7xvwVC2z280 aVCY1x0267AKxVWxJr0_GcWle2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4 CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_JrI_JrylYx0Ex4A2jsIE14v26r1j6r4UMcvj eVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x0EwIxGrwCY02Avz4vE14v_Gr4l42xK82IYc2 Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_Gr1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s02 6x8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r126r1DMIIYrxkI7VAKI48JMIIF0x vE2Ix0cI8IcVAFwI0_Jr0_JF4lIxAIcVC0I7IYx2IY6xkF7I0E14v26r1j6r4UMIIF0xvE 42xK8VAvwI8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxVWUJVW8JwCI42IY6I8E87Iv6x kF7I0E14v26r1j6r4UYxBIdaVFxhVjvjDU0xZFpf9x07bOYFAUUUUU= X-CM-SenderInfo: p1dqw3xlh2x3gn0dqz5rrqw2lrqou0/ Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Kprobes allows you to trap at almost any kernel address and execute a callback function, this commit adds kprobe support for LoongArch. Signed-off-by: Tiezhu Yang --- arch/loongarch/Kconfig | 1 + arch/loongarch/include/asm/inst.h | 31 ++++ arch/loongarch/include/asm/kprobes.h | 44 +++++ arch/loongarch/include/asm/ptrace.h | 1 + arch/loongarch/kernel/Makefile | 2 + arch/loongarch/kernel/inst.c | 107 ++++++++++++ arch/loongarch/kernel/kprobes.c | 309 +++++++++++++++++++++++++++++++= ++++ 7 files changed, 495 insertions(+) create mode 100644 arch/loongarch/include/asm/kprobes.h create mode 100644 arch/loongarch/kernel/kprobes.c diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 5b5fc94..6b01073 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -94,6 +94,7 @@ config LOONGARCH select HAVE_IOREMAP_PROT select HAVE_IRQ_EXIT_ON_IRQ_STACK select HAVE_IRQ_TIME_ACCOUNTING + select HAVE_KPROBES select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI select HAVE_PCI diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm= /inst.h index fce1843..fc8879b 100644 --- a/arch/loongarch/include/asm/inst.h +++ b/arch/loongarch/include/asm/inst.h @@ -7,6 +7,7 @@ =20 #include #include +#include =20 #define INSN_BREAK 0x002a0000 =20 @@ -20,6 +21,10 @@ =20 #define ADDR_IMM(addr, INSN) ((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIF= T_##INSN) =20 +enum reg0i15_op { + break_op =3D 0x54, +}; + enum reg0i26_op { b_op =3D 0x14, bl_op =3D 0x15, @@ -28,6 +33,8 @@ enum reg0i26_op { enum reg1i20_op { lu12iw_op =3D 0x0a, lu32id_op =3D 0x0b, + pcaddi_op =3D 0x0c, + pcalau12i_op =3D 0x0d, pcaddu12i_op =3D 0x0e, pcaddu18i_op =3D 0x0f, }; @@ -35,6 +42,8 @@ enum reg1i20_op { enum reg1i21_op { beqz_op =3D 0x10, bnez_op =3D 0x11, + bceqz_op =3D 0x48000000, + bcnez_op =3D 0x48000100, }; =20 enum reg2_op { @@ -164,6 +173,11 @@ enum reg3sa2_op { alsld_op =3D 0x16, }; =20 +struct reg0i15_format { + unsigned int immediate : 15; + unsigned int opcode : 17; +}; + struct reg0i26_format { unsigned int immediate_h : 10; unsigned int immediate_l : 16; @@ -249,6 +263,7 @@ struct reg3sa2_format { =20 union loongarch_instruction { unsigned int word; + struct reg0i15_format reg0i15_format; struct reg0i26_format reg0i26_format; struct reg1i20_format reg1i20_format; struct reg1i21_format reg1i21_format; @@ -313,6 +328,12 @@ static inline bool is_branch_ins(union loongarch_instr= uction *ip) ip->reg1i21_format.opcode <=3D bgeu_op; } =20 +static inline bool is_pc_ins(union loongarch_instruction *ip) +{ + return ip->reg1i20_format.opcode >=3D pcaddi_op && + ip->reg1i20_format.opcode <=3D pcaddu18i_op; +} + static inline bool is_ra_save_ins(union loongarch_instruction *ip) { /* st.d $ra, $sp, offset */ @@ -334,6 +355,16 @@ static inline bool is_stack_alloc_ins(union loongarch_= instruction *ip) u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm); u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, in= t imm); u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsi= gned long pc, unsigned long dest); +void simu_branch(struct pt_regs *regs, union loongarch_instruction insn); +void simu_pc(struct pt_regs *regs, union loongarch_instruction insn); + +static inline unsigned long sign_extended(unsigned long val, unsigned int = idx) +{ + if (val & (1UL << idx)) + return ~((1UL << (idx + 1)) - 1) | val; + else + return ((1UL << (idx + 1)) - 1) & val; +} =20 static inline bool signed_imm_check(long val, unsigned int bit) { diff --git a/arch/loongarch/include/asm/kprobes.h b/arch/loongarch/include/= asm/kprobes.h new file mode 100644 index 0000000..afcf254 --- /dev/null +++ b/arch/loongarch/include/asm/kprobes.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_LOONGARCH_KPROBES_H +#define __ASM_LOONGARCH_KPROBES_H + +#include + +#ifdef CONFIG_KPROBES + +#include + +#define __ARCH_WANT_KPROBES_INSN_SLOT +#define MAX_INSN_SIZE 2 + +#define flush_insn_slot(p) do { } while (0) +#define kretprobe_blacklist_size 0 + +typedef union loongarch_instruction kprobe_opcode_t; + +/* Architecture specific copy of original instruction */ +struct arch_specific_insn { + /* copy of the original instruction */ + kprobe_opcode_t *insn; +}; + +struct prev_kprobe { + struct kprobe *kp; + unsigned long status; + unsigned long saved_irq; + unsigned long saved_era; +}; + +/* per-cpu kprobe control block */ +struct kprobe_ctlblk { + unsigned long kprobe_status; + unsigned long kprobe_saved_irq; + unsigned long kprobe_saved_era; + struct prev_kprobe prev_kprobe; +}; + +void arch_remove_kprobe(struct kprobe *p); +bool kprobe_fault_handler(struct pt_regs *regs, int trapnr); + +#endif /* CONFIG_KPROBES */ +#endif /* __ASM_LOONGARCH_KPROBES_H */ diff --git a/arch/loongarch/include/asm/ptrace.h b/arch/loongarch/include/a= sm/ptrace.h index 17838c6..eb9538a 100644 --- a/arch/loongarch/include/asm/ptrace.h +++ b/arch/loongarch/include/asm/ptrace.h @@ -6,6 +6,7 @@ #define _ASM_PTRACE_H =20 #include +#include #include #include =20 diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 5358144..ff98d8a 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -33,4 +33,6 @@ obj-$(CONFIG_UNWINDER_PROLOGUE) +=3D unwind_prologue.o =20 obj-$(CONFIG_PERF_EVENTS) +=3D perf_event.o perf_regs.o =20 +obj-$(CONFIG_KPROBES) +=3D kprobes.o + CPPFLAGS_vmlinux.lds :=3D $(KBUILD_CFLAGS) diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c index b1df0ec..143acb6 100644 --- a/arch/loongarch/kernel/inst.c +++ b/arch/loongarch/kernel/inst.c @@ -38,3 +38,110 @@ u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loo= ngarch_gpr rj, unsigned l =20 return insn.word; } + +void simu_branch(struct pt_regs *regs, union loongarch_instruction insn) +{ + unsigned int imm, imm_l, imm_h, rd, rj; + unsigned long pc =3D regs->csr_era; + + imm_l =3D insn.reg0i26_format.immediate_l; + imm_h =3D insn.reg0i26_format.immediate_h; + switch (insn.reg0i26_format.opcode) { + case b_op: + regs->csr_era =3D pc + sign_extended((imm_h << 16 | imm_l) << 2, 27); + return; + case bl_op: + regs->csr_era =3D pc + sign_extended((imm_h << 16 | imm_l) << 2, 27); + regs->regs[1] =3D pc + LOONGARCH_INSN_SIZE; + return; + } + + imm_l =3D insn.reg1i21_format.immediate_l; + imm_h =3D insn.reg1i21_format.immediate_h; + rj =3D insn.reg1i21_format.rj; + switch (insn.reg1i21_format.opcode) { + case beqz_op: + if (regs->regs[rj] =3D=3D 0) + regs->csr_era =3D pc + sign_extended((imm_h << 16 | imm_l) << 2, 22); + else + regs->csr_era =3D pc + LOONGARCH_INSN_SIZE; + return; + case bnez_op: + if (regs->regs[rj] !=3D 0) + regs->csr_era =3D pc + sign_extended((imm_h << 16 | imm_l) << 2, 22); + else + regs->csr_era =3D pc + LOONGARCH_INSN_SIZE; + return; + } + + imm =3D insn.reg2i16_format.immediate; + rj =3D insn.reg2i16_format.rj; + rd =3D insn.reg2i16_format.rd; + switch (insn.reg2i16_format.opcode) { + case beq_op: + if (regs->regs[rj] =3D=3D regs->regs[rd]) + regs->csr_era =3D pc + sign_extended(imm << 2, 17); + else + regs->csr_era =3D pc + LOONGARCH_INSN_SIZE; + break; + case bne_op: + if (regs->regs[rj] !=3D regs->regs[rd]) + regs->csr_era =3D pc + sign_extended(imm << 2, 17); + else + regs->csr_era =3D pc + LOONGARCH_INSN_SIZE; + break; + case blt_op: + if ((long)regs->regs[rj] < (long)regs->regs[rd]) + regs->csr_era =3D pc + sign_extended(imm << 2, 17); + else + regs->csr_era =3D pc + LOONGARCH_INSN_SIZE; + break; + case bge_op: + if ((long)regs->regs[rj] >=3D (long)regs->regs[rd]) + regs->csr_era =3D pc + sign_extended(imm << 2, 17); + else + regs->csr_era =3D pc + LOONGARCH_INSN_SIZE; + break; + case bltu_op: + if (regs->regs[rj] < regs->regs[rd]) + regs->csr_era =3D pc + sign_extended(imm << 2, 17); + else + regs->csr_era =3D pc + LOONGARCH_INSN_SIZE; + break; + case bgeu_op: + if (regs->regs[rj] >=3D regs->regs[rd]) + regs->csr_era =3D pc + sign_extended(imm << 2, 17); + else + regs->csr_era =3D pc + LOONGARCH_INSN_SIZE; + break; + case jirl_op: + regs->csr_era =3D regs->regs[rj] + sign_extended(imm << 2, 17); + regs->regs[rd] =3D pc + LOONGARCH_INSN_SIZE; + break; + } +} + +void simu_pc(struct pt_regs *regs, union loongarch_instruction insn) +{ + unsigned long pc =3D regs->csr_era; + unsigned int rd =3D insn.reg1i20_format.rd; + unsigned int imm =3D insn.reg1i20_format.immediate; + + switch (insn.reg1i20_format.opcode) { + case pcaddi_op: + regs->regs[rd] =3D pc + sign_extended(imm << 2, 21); + break; + case pcaddu12i_op: + regs->regs[rd] =3D pc + sign_extended(imm << 12, 31); + break; + case pcaddu18i_op: + regs->regs[rd] =3D pc + sign_extended(imm << 18, 37); + break; + case pcalau12i_op: + regs->regs[rd] =3D pc + sign_extended(imm << 12, 31); + regs->regs[rd] &=3D ~((1 << 12) - 1); + break; + } + + regs->csr_era +=3D LOONGARCH_INSN_SIZE; +} diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobe= s.c new file mode 100644 index 0000000..b2c73a5 --- /dev/null +++ b/arch/loongarch/kernel/kprobes.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include + +static const union loongarch_instruction breakpoint_insn =3D { + .reg0i15_format =3D { + .opcode =3D break_op, + .immediate =3D BRK_KPROBE_BP, + } +}; + +static const union loongarch_instruction singlestep_insn =3D { + .reg0i15_format =3D { + .opcode =3D break_op, + .immediate =3D BRK_KPROBE_SSTEPBP, + } +}; + +DEFINE_PER_CPU(struct kprobe *, current_kprobe); +DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); + +static bool insns_are_not_supported(union loongarch_instruction insn) +{ + switch (insn.reg2i14_format.opcode) { + case llw_op: + case lld_op: + case scw_op: + case scd_op: + pr_notice("kprobe: ll or sc instructions are not supported\n"); + return true; + } + + switch (insn.reg1i21_format.opcode) { + case bceqz_op: + case bcnez_op: + pr_notice("kprobe: bceqz or bcnez instructions are not supported\n"); + return true; + } + + return false; +} +NOKPROBE_SYMBOL(insns_are_not_supported); + +int arch_prepare_kprobe(struct kprobe *p) +{ + union loongarch_instruction insn; + + insn =3D p->addr[0]; + if (insns_are_not_supported(insn)) + return -EINVAL; + + p->ainsn.insn =3D get_insn_slot(); + if (!p->ainsn.insn) + return -ENOMEM; + + p->ainsn.insn[0] =3D *p->addr; + p->ainsn.insn[1] =3D singlestep_insn; + + p->opcode =3D *p->addr; + + return 0; +} +NOKPROBE_SYMBOL(arch_prepare_kprobe); + +/* Install breakpoint in text */ +void arch_arm_kprobe(struct kprobe *p) +{ + *p->addr =3D breakpoint_insn; +} +NOKPROBE_SYMBOL(arch_arm_kprobe); + +/* Remove breakpoint from text */ +void arch_disarm_kprobe(struct kprobe *p) +{ + *p->addr =3D p->opcode; +} +NOKPROBE_SYMBOL(arch_disarm_kprobe); + +void arch_remove_kprobe(struct kprobe *p) +{ + if (p->ainsn.insn) { + free_insn_slot(p->ainsn.insn, 0); + p->ainsn.insn =3D NULL; + } +} +NOKPROBE_SYMBOL(arch_remove_kprobe); + +static void save_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + kcb->prev_kprobe.kp =3D kprobe_running(); + kcb->prev_kprobe.status =3D kcb->kprobe_status; + kcb->prev_kprobe.saved_irq =3D kcb->kprobe_saved_irq; + kcb->prev_kprobe.saved_era =3D kcb->kprobe_saved_era; +} +NOKPROBE_SYMBOL(save_previous_kprobe); + +static void restore_previous_kprobe(struct kprobe_ctlblk *kcb) +{ + __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); + kcb->kprobe_status =3D kcb->prev_kprobe.status; + kcb->kprobe_saved_irq =3D kcb->prev_kprobe.saved_irq; + kcb->kprobe_saved_era =3D kcb->prev_kprobe.saved_era; +} +NOKPROBE_SYMBOL(restore_previous_kprobe); + +static void set_current_kprobe(struct kprobe *p, struct pt_regs *regs, + struct kprobe_ctlblk *kcb) +{ + __this_cpu_write(current_kprobe, p); + kcb->kprobe_saved_irq =3D regs->csr_prmd & CSR_PRMD_PIE; + kcb->kprobe_saved_era =3D regs->csr_era; +} +NOKPROBE_SYMBOL(set_current_kprobe); + +static bool insns_are_not_simulated(struct kprobe *p, struct pt_regs *regs) +{ + if (is_branch_ins(&p->opcode)) { + simu_branch(regs, p->opcode); + return false; + } else if (is_pc_ins(&p->opcode)) { + simu_pc(regs, p->opcode); + return false; + } else { + return true; + } +} +NOKPROBE_SYMBOL(insns_are_not_simulated); + +static void setup_singlestep(struct kprobe *p, struct pt_regs *regs, + struct kprobe_ctlblk *kcb, int reenter) +{ + if (reenter) { + save_previous_kprobe(kcb); + set_current_kprobe(p, regs, kcb); + kcb->kprobe_status =3D KPROBE_REENTER; + } else { + kcb->kprobe_status =3D KPROBE_HIT_SS; + } + + if (p->ainsn.insn->word =3D=3D breakpoint_insn.word) { + regs->csr_prmd &=3D ~CSR_PRMD_PIE; + regs->csr_prmd |=3D kcb->kprobe_saved_irq; + return; + } + + regs->csr_prmd &=3D ~CSR_PRMD_PIE; + + if (insns_are_not_simulated(p, regs)) { + kcb->kprobe_status =3D KPROBE_HIT_SS; + regs->csr_era =3D (unsigned long)&p->ainsn.insn[0]; + } else { + kcb->kprobe_status =3D KPROBE_HIT_SSDONE; + if (p->post_handler) + p->post_handler(p, regs, 0); + reset_current_kprobe(); + } +} +NOKPROBE_SYMBOL(setup_singlestep); + +static bool reenter_kprobe(struct kprobe *p, struct pt_regs *regs, + struct kprobe_ctlblk *kcb) +{ + switch (kcb->kprobe_status) { + case KPROBE_HIT_SSDONE: + case KPROBE_HIT_ACTIVE: + kprobes_inc_nmissed_count(p); + setup_singlestep(p, regs, kcb, 1); + break; + case KPROBE_HIT_SS: + case KPROBE_REENTER: + pr_warn("Failed to recover from reentered kprobes.\n"); + dump_kprobe(p); + BUG(); + break; + default: + WARN_ON(1); + return false; + } + + return true; +} +NOKPROBE_SYMBOL(reenter_kprobe); + +static bool kprobe_pre_handler(struct pt_regs *regs) +{ + struct kprobe *p, *cur_kprobe; + struct kprobe_ctlblk *kcb; + unsigned long addr =3D instruction_pointer(regs); + + kcb =3D get_kprobe_ctlblk(); + cur_kprobe =3D kprobe_running(); + + p =3D get_kprobe((kprobe_opcode_t *) addr); + if (p) { + if (cur_kprobe) { + if (reenter_kprobe(p, regs, kcb)) + return true; + } else { + /* Probe hit */ + set_current_kprobe(p, regs, kcb); + kcb->kprobe_status =3D 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, it will + * modify the execution path and no need to single + * stepping. Let's just reset current kprobe and exit. + * + * pre_handler can hit a breakpoint and can step thru + * before return. + */ + if (!p->pre_handler || !p->pre_handler(p, regs)) + setup_singlestep(p, regs, kcb, 0); + else + reset_current_kprobe(); + } + return true; + } + + /* + * The breakpoint instruction was removed right + * after we hit it. Another cpu has removed + * either a probepoint or a debugger breakpoint + * at this address. In either case, no further + * handling of this interrupt is appropriate. + * Return back to original instruction, and continue. + */ + return false; +} +NOKPROBE_SYMBOL(kprobe_pre_handler); + +static inline bool kprobe_post_handler(struct pt_regs *regs) +{ + struct kprobe *cur =3D kprobe_running(); + struct kprobe_ctlblk *kcb =3D get_kprobe_ctlblk(); + + if (!cur) + return false; + + /* Restore back the original saved kprobes variables and continue */ + if (kcb->kprobe_status =3D=3D KPROBE_REENTER) { + restore_previous_kprobe(kcb); + return true; + } + + /* Call post handler */ + kcb->kprobe_status =3D KPROBE_HIT_SSDONE; + if (cur->post_handler) + cur->post_handler(cur, regs, 0); + + regs->csr_era =3D kcb->kprobe_saved_era + LOONGARCH_INSN_SIZE; + regs->csr_prmd |=3D kcb->kprobe_saved_irq; + + reset_current_kprobe(); + + return true; +} +NOKPROBE_SYMBOL(kprobe_post_handler); + +bool kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + struct kprobe_ctlblk *kcb =3D get_kprobe_ctlblk(); + + if (kcb->kprobe_status & KPROBE_HIT_SS) { + regs->csr_era =3D kcb->kprobe_saved_era + LOONGARCH_INSN_SIZE; + regs->csr_prmd |=3D kcb->kprobe_saved_irq; + reset_current_kprobe(); + } + + return false; +} +NOKPROBE_SYMBOL(kprobe_fault_handler); + +int kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct die_args *args =3D (struct die_args *)data; + int ret =3D NOTIFY_DONE; + + switch (val) { + case DIE_BREAK: + if (kprobe_pre_handler(args->regs)) + ret =3D NOTIFY_STOP; + break; + case DIE_SSTEPBP: + if (kprobe_post_handler(args->regs)) + ret =3D NOTIFY_STOP; + break; + case DIE_PAGE_FAULT: + preempt_disable(); + if (kprobe_running() + && kprobe_fault_handler(args->regs, args->trapnr)) + ret =3D NOTIFY_STOP; + preempt_enable(); + break; + default: + break; + } + + return ret; +} +NOKPROBE_SYMBOL(kprobe_exceptions_notify); + +int __init arch_init_kprobes(void) +{ + return 0; +} --=20 2.1.0 From nobody Thu Apr 2 23:02:05 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B8EF2C54EE9 for ; Tue, 20 Sep 2022 03:37:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229987AbiITDh3 (ORCPT ); Mon, 19 Sep 2022 23:37:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47994 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229596AbiITDhS (ORCPT ); Mon, 19 Sep 2022 23:37:18 -0400 Received: from loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id EA4E15A14A for ; Mon, 19 Sep 2022 20:37:16 -0700 (PDT) Received: from linux.localdomain (unknown [113.200.148.30]) by localhost.localdomain (Coremail) with SMTP id AQAAf8DxReJqNSlj7FseAA--.48978S4; Tue, 20 Sep 2022 11:37:15 +0800 (CST) From: Tiezhu Yang To: Huacai Chen , Masami Hiramatsu Cc: loongarch@lists.linux.dev, linux-kernel@vger.kernel.org Subject: [PATCH 2/4] LoongArch: Add kretprobe support Date: Tue, 20 Sep 2022 11:37:12 +0800 Message-Id: <1663645034-967-3-git-send-email-yangtiezhu@loongson.cn> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1663645034-967-1-git-send-email-yangtiezhu@loongson.cn> References: <1663645034-967-1-git-send-email-yangtiezhu@loongson.cn> X-CM-TRANSID: AQAAf8DxReJqNSlj7FseAA--.48978S4 X-Coremail-Antispam: 1UD129KBjvJXoWxGFWxAw1kuFWxJF1UWF4Uurg_yoW5Gw1fpF 9Iyr9xGr45WrZ3ZrZxJ34FvF4S9rn7u3y7GrZrJa4rCFy5XryUJr1xXrW2vFyrGrZYvr1S qF4rJryYkFW7J37anT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUBFb7Iv0xC_KF4lb4IE77IF4wAFF20E14v26r4j6ryUM7CY07I2 0VC2zVCF04k26cxKx2IYs7xG6rWj6s0DM7CIcVAFz4kK6r1j6r18M28IrcIa0xkI8VA2jI 8067AKxVWUXwA2048vs2IY020Ec7CjxVAFwI0_JFI_Gr1l8cAvFVAK0II2c7xJM28CjxkF 64kEwVA0rcxSw2x7M28EF7xvwVC0I7IYx2IY67AKxVW5JVW7JwA2z4x0Y4vE2Ix0cI8IcV CY1x0267AKxVW8JVWxJwA2z4x0Y4vEx4A2jsIE14v26r4UJVWxJr1l84ACjcxK6I8E87Iv 6xkF7I0E14v26F4UJVW0owAS0I0E0xvYzxvE52x082IY62kv0487Mc02F40EFcxC0VAKzV Aqx4xG6I80ewAv7VC0I7IYx2IY67AKxVWUGVWUXwAv7VC2z280aVAFwI0_Jr0_Gr1lOx8S 6xCaFVCjc4AY6r1j6r4UM4x0Y48IcxkI7VAKI48JMxkIecxEwVAFwVW8GwCF04k20xvY0x 0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18MI8I3I0E 7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_JF0_Jw1lIxkGc2Ij64vIr41lIxAIcV C0I7IYx2IY67AKxVWUJVWUCwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1lIxAIcVCF 04k26cxKx2IYs7xG6r1j6r1xMIIF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4A2jsIEc7 CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVjvjDU0xZFpf9x07jw4SrUUUUU= X-CM-SenderInfo: p1dqw3xlh2x3gn0dqz5rrqw2lrqou0/ Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Use the generic kretprobe trampoline handler to add kretprobe support for LoongArch. Signed-off-by: Tiezhu Yang --- arch/loongarch/Kconfig | 1 + arch/loongarch/kernel/kprobes.c | 49 +++++++++++++++++++++++++++++++++++++= +++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 6b01073..5f8503a 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -95,6 +95,7 @@ config LOONGARCH select HAVE_IRQ_EXIT_ON_IRQ_STACK select HAVE_IRQ_TIME_ACCOUNTING select HAVE_KPROBES + select HAVE_KRETPROBES select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI select HAVE_PCI diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobe= s.c index b2c73a5..8c3efe5 100644 --- a/arch/loongarch/kernel/kprobes.c +++ b/arch/loongarch/kernel/kprobes.c @@ -303,7 +303,54 @@ int kprobe_exceptions_notify(struct notifier_block *se= lf, } NOKPROBE_SYMBOL(kprobe_exceptions_notify); =20 +/* + * For function-return probes, init_kprobes() establishes a probepoint + * here. When a retprobed function returns, this probe is hit and + * trampoline_probe_handler() runs, calling the kretprobe's handler. + */ +static void __used kretprobe_trampoline_holder(void) +{ + asm volatile(".global __kretprobe_trampoline\n" + "__kretprobe_trampoline:\n" + "nop\n"); +} + +/* Called when the probe at kretprobe trampoline is hit */ +static int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) +{ + instruction_pointer(regs) =3D __kretprobe_trampoline_handler(regs, NULL); + /* + * By returning a non-zero value, we are telling + * kprobe_handler() that we don't want the post_handler + * to run (and have re-enabled preemption) + */ + return 1; +} +NOKPROBE_SYMBOL(trampoline_probe_handler); + +static struct kprobe trampoline_p =3D { + .addr =3D (kprobe_opcode_t *)__kretprobe_trampoline, + .pre_handler =3D trampoline_probe_handler +}; + +void arch_prepare_kretprobe(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + ri->ret_addr =3D (kprobe_opcode_t *)regs->regs[1]; + ri->fp =3D NULL; + + /* Replace the return addr with trampoline addr */ + regs->regs[1] =3D (unsigned long)__kretprobe_trampoline; +} +NOKPROBE_SYMBOL(arch_prepare_kretprobe); + +int arch_trampoline_kprobe(struct kprobe *p) +{ + return p->addr =3D=3D trampoline_p.addr; +} +NOKPROBE_SYMBOL(arch_trampoline_kprobe); + int __init arch_init_kprobes(void) { - return 0; + return register_kprobe(&trampoline_p); } --=20 2.1.0 From nobody Thu Apr 2 23:02:05 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7A5D7ECAAD8 for ; Tue, 20 Sep 2022 03:37:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230003AbiITDhj (ORCPT ); Mon, 19 Sep 2022 23:37:39 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48000 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229816AbiITDhT (ORCPT ); Mon, 19 Sep 2022 23:37:19 -0400 Received: from loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 408255A14C for ; Mon, 19 Sep 2022 20:37:17 -0700 (PDT) Received: from linux.localdomain (unknown [113.200.148.30]) by localhost.localdomain (Coremail) with SMTP id AQAAf8DxReJqNSlj7FseAA--.48978S5; Tue, 20 Sep 2022 11:37:16 +0800 (CST) From: Tiezhu Yang To: Huacai Chen , Masami Hiramatsu Cc: loongarch@lists.linux.dev, linux-kernel@vger.kernel.org Subject: [PATCH 3/4] samples/kprobes: Add LoongArch support Date: Tue, 20 Sep 2022 11:37:13 +0800 Message-Id: <1663645034-967-4-git-send-email-yangtiezhu@loongson.cn> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1663645034-967-1-git-send-email-yangtiezhu@loongson.cn> References: <1663645034-967-1-git-send-email-yangtiezhu@loongson.cn> X-CM-TRANSID: AQAAf8DxReJqNSlj7FseAA--.48978S5 X-Coremail-Antispam: 1UD129KBjvJXoWrZw4rWrW5CF1fJw1xZFWDJwb_yoW8JF1fpF n0y3W5t3yFyw13WFW3Jayvgry0yryjkay8u3ykC34Yya429ry5AF1rKayjyw4kur90qF43 tr1FvryUGF1xZrJanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUB2b7Iv0xC_Cr1lb4IE77IF4wAFF20E14v26ryj6rWUM7CY07I2 0VC2zVCF04k26cxKx2IYs7xG6rWj6s0DM7CIcVAFz4kK6r1j6r18M28IrcIa0xkI8VA2jI 8067AKxVWUWwA2048vs2IY020Ec7CjxVAFwI0_Gr0_Xr1l8cAvFVAK0II2c7xJM28CjxkF 64kEwVA0rcxSw2x7M28EF7xvwVC0I7IYx2IY67AKxVW5JVW7JwA2z4x0Y4vE2Ix0cI8IcV CY1x0267AKxVWxJVW8Jr1l84ACjcxK6I8E87Iv67AKxVW8Jr0_Cr1UM28EF7xvwVC2z280 aVCY1x0267AKxVWxJr0_GcWle2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4 CE5I8CrVC2j2WlYx0E2Ix0cI8IcVAFwI0_JrI_JrylYx0Ex4A2jsIE14v26r1j6r4UMcvj eVCFs4IE7xkEbVWUJVW8JwACjcxG0xvY0x0EwIxGrwCY02Avz4vE14v_Gr4l42xK82IYc2 Ij64vIr41l4I8I3I0E4IkC6x0Yz7v_Jr0_Gr1lx2IqxVAqx4xG67AKxVWUJVWUGwC20s02 6x8GjcxK67AKxVWUGVWUWwC2zVAF1VAY17CE14v26r126r1DMIIYrxkI7VAKI48JMIIF0x vE2Ix0cI8IcVAFwI0_Jr0_JF4lIxAIcVC0I7IYx2IY6xkF7I0E14v26r4j6F4UMIIF0xvE 42xK8VAvwI8IcIk0rVWUJVWUCwCI42IY6I8E87Iv67AKxVWUJVW8JwCI42IY6I8E87Iv6x kF7I0E14v26r4j6r4UJbIYCTnIWIevJa73UjIFyTuYvjxUI5fHUUUUU X-CM-SenderInfo: p1dqw3xlh2x3gn0dqz5rrqw2lrqou0/ Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Add LoongArch specific info in handler_pre() and handler_post(). Signed-off-by: Tiezhu Yang --- samples/kprobes/kprobe_example.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/samples/kprobes/kprobe_example.c b/samples/kprobes/kprobe_exam= ple.c index fd346f5..ef44c61 100644 --- a/samples/kprobes/kprobe_example.c +++ b/samples/kprobes/kprobe_example.c @@ -55,6 +55,10 @@ static int __kprobes handler_pre(struct kprobe *p, struc= t pt_regs *regs) pr_info("<%s> p->addr, 0x%p, ip =3D 0x%lx, flags =3D 0x%lx\n", p->symbol_name, p->addr, regs->psw.addr, regs->flags); #endif +#ifdef CONFIG_LOONGARCH + pr_info("<%s> p->addr =3D 0x%p, era =3D 0x%lx, estat =3D 0x%lx\n", + p->symbol_name, p->addr, regs->csr_era, regs->csr_estat); +#endif =20 /* A dump_stack() here will give a stack backtrace */ return 0; @@ -92,6 +96,10 @@ static void __kprobes handler_post(struct kprobe *p, str= uct pt_regs *regs, pr_info("<%s> p->addr, 0x%p, flags =3D 0x%lx\n", p->symbol_name, p->addr, regs->flags); #endif +#ifdef CONFIG_LOONGARCH + pr_info("<%s> p->addr =3D 0x%p, estat =3D 0x%lx\n", + p->symbol_name, p->addr, regs->csr_estat); +#endif } =20 static int __init kprobe_init(void) --=20 2.1.0 From nobody Thu Apr 2 23:02:05 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0C818ECAAD8 for ; Tue, 20 Sep 2022 03:37:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229973AbiITDhd (ORCPT ); Mon, 19 Sep 2022 23:37:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48002 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229776AbiITDhT (ORCPT ); Mon, 19 Sep 2022 23:37:19 -0400 Received: from loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id D2E5B57544 for ; Mon, 19 Sep 2022 20:37:17 -0700 (PDT) Received: from linux.localdomain (unknown [113.200.148.30]) by localhost.localdomain (Coremail) with SMTP id AQAAf8DxReJqNSlj7FseAA--.48978S6; Tue, 20 Sep 2022 11:37:16 +0800 (CST) From: Tiezhu Yang To: Huacai Chen , Masami Hiramatsu Cc: loongarch@lists.linux.dev, linux-kernel@vger.kernel.org Subject: [PATCH 4/4] LoongArch: Enable KPROBES in default config Date: Tue, 20 Sep 2022 11:37:14 +0800 Message-Id: <1663645034-967-5-git-send-email-yangtiezhu@loongson.cn> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1663645034-967-1-git-send-email-yangtiezhu@loongson.cn> References: <1663645034-967-1-git-send-email-yangtiezhu@loongson.cn> X-CM-TRANSID: AQAAf8DxReJqNSlj7FseAA--.48978S6 X-Coremail-Antispam: 1UD129KBjvdXoW7Gw47JFykCFWDWFW7Jr1xuFg_yoW3WFg_JF y7Kws5WrWxJa97u34Iqw4rGw4DA3WUZ3WYkFnrJr1xW3W3tw13Jr4Dtw13C3Z0gayq9rsx XaykAF9I9F10yjkaLaAFLSUrUUUUUb8apTn2vfkv8UJUUUU8Yxn0WfASr-VFAUDa7-sFnT 9fnUUIcSsGvfJTRUUUbSAYjsxI4VWxJwAYFVCjjxCrM7AC8VAFwI0_Wr0E3s1l1xkIjI8I 6I8E6xAIw20EY4v20xvaj40_Wr0E3s1l1IIY67AEw4v_Jr0_Jr4l82xGYIkIc2x26280x7 IE14v26r126s0DM28IrcIa0xkI8VCY1x0267AKxVW8JVW5JwA2ocxC64kIII0Yj41l84x0 c7CEw4AK67xGY2AK021l84ACjcxK6xIIjxv20xvE14v26ryj6F1UM28EF7xvwVC0I7IYx2 IY6xkF7I0E14v26F4j6r4UJwA2z4x0Y4vEx4A2jsIE14v26r4UJVWxJr1l84ACjcxK6I8E 87Iv6xkF7I0E14v26F4UJVW0owAS0I0E0xvYzxvE52x082IY62kv0487Mc02F40EFcxC0V AKzVAqx4xG6I80ewAv7VC0I7IYx2IY67AKxVWUGVWUXwAv7VC2z280aVAFwI0_Jr0_Gr1l Ox8S6xCaFVCjc4AY6r1j6r4UM4x0Y48IcxkI7VAKI48JMxkIecxEwVAFwVW8GwCF04k20x vY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18MI8I 3I0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_JF0_Jw1lIxkGc2Ij64vIr41lIx AIcVC0I7IYx2IY67AKxVWUJVWUCwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr0_Cr1lIxAI cVCF04k26cxKx2IYs7xG6r1j6r1xMIIF0xvEx4A2jsIE14v26r1j6r4UMIIF0xvEx4A2js IEc7CjxVAFwI0_Gr0_Gr1UYxBIdaVFxhVjvjDU0xZFpf9x07jxvtZUUUUU= X-CM-SenderInfo: p1dqw3xlh2x3gn0dqz5rrqw2lrqou0/ Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Kprobes for LoongArch is supported now, update loongson3_defconfig to enable KPROBES. Signed-off-by: Tiezhu Yang --- arch/loongarch/configs/loongson3_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/co= nfigs/loongson3_defconfig index c96ce64..8c45043 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -60,6 +60,7 @@ CONFIG_ACPI_HOTPLUG_MEMORY=3Dy CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=3Dy CONFIG_EFI_CAPSULE_LOADER=3Dm CONFIG_EFI_TEST=3Dm +CONFIG_KPROBES=3Dy CONFIG_MODULES=3Dy CONFIG_MODULE_FORCE_LOAD=3Dy CONFIG_MODULE_UNLOAD=3Dy --=20 2.1.0