linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch
@ 2022-09-28  0:50 Tiezhu Yang
  2022-09-28  0:50 ` [PATCH v2 1/5] LoongArch: Simulate branch and PC instructions Tiezhu Yang
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: Tiezhu Yang @ 2022-09-28  0:50 UTC (permalink / raw)
  To: Huacai Chen, Masami Hiramatsu; +Cc: loongarch, linux-kernel

v2:
  -- Split simu_branch() and simu_pc() into a single patch
  -- Call kprobe_page_fault() in do_page_fault()
  -- Add kprobes_trampoline.S for kretprobe

Tiezhu Yang (5):
  LoongArch: Simulate branch and PC instructions
  LoongArch: Add kprobe support
  LoongArch: Add kretprobe support
  samples/kprobes: Add LoongArch support
  LoongArch: Enable KPROBES in default config

 arch/loongarch/Kconfig                     |   2 +
 arch/loongarch/configs/loongson3_defconfig |   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               | 113 ++++++++++
 arch/loongarch/kernel/kprobes.c            | 336 +++++++++++++++++++++++++++++
 arch/loongarch/kernel/kprobes_trampoline.S |  97 +++++++++
 arch/loongarch/mm/fault.c                  |   3 +
 samples/kprobes/kprobe_example.c           |   8 +
 11 files changed, 638 insertions(+)
 create mode 100644 arch/loongarch/include/asm/kprobes.h
 create mode 100644 arch/loongarch/kernel/kprobes.c
 create mode 100644 arch/loongarch/kernel/kprobes_trampoline.S

-- 
2.1.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v2 1/5] LoongArch: Simulate branch and PC instructions
  2022-09-28  0:50 [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Tiezhu Yang
@ 2022-09-28  0:50 ` Tiezhu Yang
  2022-11-14  4:37   ` Huacai Chen
  2022-09-28  0:50 ` [PATCH v2 2/5] LoongArch: Add kprobe support Tiezhu Yang
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Tiezhu Yang @ 2022-09-28  0:50 UTC (permalink / raw)
  To: Huacai Chen, Masami Hiramatsu; +Cc: loongarch, linux-kernel

According to LoongArch Reference Manual, simulate branch and
PC instructions, this is preparation for later patch.

Link: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#branch-instructions
Link: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_pcaddi_pcaddu121_pcaddu18l_pcalau12i

Co-developed-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Jinyang He <hejinyang@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
---
 arch/loongarch/include/asm/inst.h   |  19 ++++++
 arch/loongarch/include/asm/ptrace.h |   1 +
 arch/loongarch/kernel/inst.c        | 113 ++++++++++++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)

diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
index fce1843..93d5cd4 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -7,6 +7,7 @@
 
 #include <linux/types.h>
 #include <asm/asm.h>
+#include <asm/ptrace.h>
 
 #define INSN_BREAK		0x002a0000
 
@@ -28,6 +29,8 @@ enum reg0i26_op {
 enum reg1i20_op {
 	lu12iw_op	= 0x0a,
 	lu32id_op	= 0x0b,
+	pcaddi_op	= 0x0c,
+	pcalau12i_op	= 0x0d,
 	pcaddu12i_op	= 0x0e,
 	pcaddu18i_op	= 0x0f,
 };
@@ -313,6 +316,12 @@ static inline bool is_branch_ins(union loongarch_instruction *ip)
 		ip->reg1i21_format.opcode <= bgeu_op;
 }
 
+static inline bool is_pc_ins(union loongarch_instruction *ip)
+{
+	return ip->reg1i20_format.opcode >= pcaddi_op &&
+		ip->reg1i20_format.opcode <= pcaddu18i_op;
+}
+
 static inline bool is_ra_save_ins(union loongarch_instruction *ip)
 {
 	/* st.d $ra, $sp, offset */
@@ -334,6 +343,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, int imm);
 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned 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;
+}
 
 static inline bool signed_imm_check(long val, unsigned int bit)
 {
diff --git a/arch/loongarch/include/asm/ptrace.h b/arch/loongarch/include/asm/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
 
 #include <asm/page.h>
+#include <asm/irqflags.h>
 #include <asm/thread_info.h>
 #include <uapi/asm/ptrace.h>
 
diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c
index b1df0ec..f5c1eff6 100644
--- a/arch/loongarch/kernel/inst.c
+++ b/arch/loongarch/kernel/inst.c
@@ -38,3 +38,116 @@ u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned l
 
 	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 = regs->csr_era;
+
+	imm_l = insn.reg0i26_format.immediate_l;
+	imm_h = insn.reg0i26_format.immediate_h;
+	switch (insn.reg0i26_format.opcode) {
+	case b_op:
+		regs->csr_era = pc + sign_extended((imm_h << 16 | imm_l) << 2, 27);
+		return;
+	case bl_op:
+		regs->csr_era = pc + sign_extended((imm_h << 16 | imm_l) << 2, 27);
+		regs->regs[1] = pc + LOONGARCH_INSN_SIZE;
+		return;
+	}
+
+	imm_l = insn.reg1i21_format.immediate_l;
+	imm_h = insn.reg1i21_format.immediate_h;
+	rj = insn.reg1i21_format.rj;
+	switch (insn.reg1i21_format.opcode) {
+	case beqz_op:
+		if (regs->regs[rj] == 0)
+			regs->csr_era = pc + sign_extended((imm_h << 16 | imm_l) << 2, 22);
+		else
+			regs->csr_era = pc + LOONGARCH_INSN_SIZE;
+		return;
+	case bnez_op:
+		if (regs->regs[rj] != 0)
+			regs->csr_era = pc + sign_extended((imm_h << 16 | imm_l) << 2, 22);
+		else
+			regs->csr_era = pc + LOONGARCH_INSN_SIZE;
+		return;
+	}
+
+	imm = insn.reg2i16_format.immediate;
+	rj = insn.reg2i16_format.rj;
+	rd = insn.reg2i16_format.rd;
+	switch (insn.reg2i16_format.opcode) {
+	case beq_op:
+		if (regs->regs[rj] == regs->regs[rd])
+			regs->csr_era = pc + sign_extended(imm << 2, 17);
+		else
+			regs->csr_era = pc + LOONGARCH_INSN_SIZE;
+		break;
+	case bne_op:
+		if (regs->regs[rj] != regs->regs[rd])
+			regs->csr_era = pc + sign_extended(imm << 2, 17);
+		else
+			regs->csr_era = pc + LOONGARCH_INSN_SIZE;
+		break;
+	case blt_op:
+		if ((long)regs->regs[rj] < (long)regs->regs[rd])
+			regs->csr_era = pc + sign_extended(imm << 2, 17);
+		else
+			regs->csr_era = pc + LOONGARCH_INSN_SIZE;
+		break;
+	case bge_op:
+		if ((long)regs->regs[rj] >= (long)regs->regs[rd])
+			regs->csr_era = pc + sign_extended(imm << 2, 17);
+		else
+			regs->csr_era = pc + LOONGARCH_INSN_SIZE;
+		break;
+	case bltu_op:
+		if (regs->regs[rj] < regs->regs[rd])
+			regs->csr_era = pc + sign_extended(imm << 2, 17);
+		else
+			regs->csr_era = pc + LOONGARCH_INSN_SIZE;
+		break;
+	case bgeu_op:
+		if (regs->regs[rj] >= regs->regs[rd])
+			regs->csr_era = pc + sign_extended(imm << 2, 17);
+		else
+			regs->csr_era = pc + LOONGARCH_INSN_SIZE;
+		break;
+	case jirl_op:
+		regs->csr_era = regs->regs[rj] + sign_extended(imm << 2, 17);
+		regs->regs[rd] = pc + LOONGARCH_INSN_SIZE;
+		break;
+	default:
+		pr_info("%s: unknown opcode\n", __func__);
+		return;
+	}
+}
+
+void simu_pc(struct pt_regs *regs, union loongarch_instruction insn)
+{
+	unsigned long pc = regs->csr_era;
+	unsigned int rd = insn.reg1i20_format.rd;
+	unsigned int imm = insn.reg1i20_format.immediate;
+
+	switch (insn.reg1i20_format.opcode) {
+	case pcaddi_op:
+		regs->regs[rd] = pc + sign_extended(imm << 2, 21);
+		break;
+	case pcaddu12i_op:
+		regs->regs[rd] = pc + sign_extended(imm << 12, 31);
+		break;
+	case pcaddu18i_op:
+		regs->regs[rd] = pc + sign_extended(imm << 18, 37);
+		break;
+	case pcalau12i_op:
+		regs->regs[rd] = pc + sign_extended(imm << 12, 31);
+		regs->regs[rd] &= ~((1 << 12) - 1);
+		break;
+	default:
+		pr_info("%s: unknown opcode\n", __func__);
+		return;
+	}
+
+	regs->csr_era += LOONGARCH_INSN_SIZE;
+}
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2 2/5] LoongArch: Add kprobe support
  2022-09-28  0:50 [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Tiezhu Yang
  2022-09-28  0:50 ` [PATCH v2 1/5] LoongArch: Simulate branch and PC instructions Tiezhu Yang
@ 2022-09-28  0:50 ` Tiezhu Yang
  2022-11-14  4:41   ` Huacai Chen
  2022-09-28  0:50 ` [PATCH v2 3/5] LoongArch: Add kretprobe support Tiezhu Yang
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Tiezhu Yang @ 2022-09-28  0:50 UTC (permalink / raw)
  To: Huacai Chen, Masami Hiramatsu; +Cc: loongarch, linux-kernel

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 <yangtiezhu@loongson.cn>
---
 arch/loongarch/Kconfig               |   1 +
 arch/loongarch/include/asm/inst.h    |  12 ++
 arch/loongarch/include/asm/kprobes.h |  44 +++++
 arch/loongarch/kernel/Makefile       |   2 +
 arch/loongarch/kernel/kprobes.c      | 312 +++++++++++++++++++++++++++++++++++
 arch/loongarch/mm/fault.c            |   3 +
 6 files changed, 374 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 be8389f..8debd70 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 93d5cd4..fc8879b 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -21,6 +21,10 @@
 
 #define ADDR_IMM(addr, INSN)	((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN)
 
+enum reg0i15_op {
+	break_op	= 0x54,
+};
+
 enum reg0i26_op {
 	b_op		= 0x14,
 	bl_op		= 0x15,
@@ -38,6 +42,8 @@ enum reg1i20_op {
 enum reg1i21_op {
 	beqz_op		= 0x10,
 	bnez_op		= 0x11,
+	bceqz_op	= 0x48000000,
+	bcnez_op	= 0x48000100,
 };
 
 enum reg2_op {
@@ -167,6 +173,11 @@ enum reg3sa2_op {
 	alsld_op	= 0x16,
 };
 
+struct reg0i15_format {
+	unsigned int immediate : 15;
+	unsigned int opcode : 17;
+};
+
 struct reg0i26_format {
 	unsigned int immediate_h : 10;
 	unsigned int immediate_l : 16;
@@ -252,6 +263,7 @@ struct reg3sa2_format {
 
 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;
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 <asm-generic/kprobes.h>
+
+#ifdef CONFIG_KPROBES
+
+#include <asm/inst.h>
+
+#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/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) += unwind_prologue.o
 
 obj-$(CONFIG_PERF_EVENTS)	+= perf_event.o perf_regs.o
 
+obj-$(CONFIG_KPROBES)		+= kprobes.o
+
 CPPFLAGS_vmlinux.lds		:= $(KBUILD_CFLAGS)
diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
new file mode 100644
index 0000000..c11f6e0
--- /dev/null
+++ b/arch/loongarch/kernel/kprobes.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/kprobes.h>
+#include <linux/kdebug.h>
+#include <asm/break.h>
+
+static const union loongarch_instruction breakpoint_insn = {
+	.reg0i15_format = {
+		.opcode = break_op,
+		.immediate = BRK_KPROBE_BP,
+	}
+};
+
+static const union loongarch_instruction singlestep_insn = {
+	.reg0i15_format = {
+		.opcode = break_op,
+		.immediate = 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 = p->addr[0];
+	if (insns_are_not_supported(insn))
+		return -EINVAL;
+
+	p->ainsn.insn = get_insn_slot();
+	if (!p->ainsn.insn)
+		return -ENOMEM;
+
+	p->ainsn.insn[0] = *p->addr;
+	p->ainsn.insn[1] = singlestep_insn;
+
+	p->opcode = *p->addr;
+
+	return 0;
+}
+NOKPROBE_SYMBOL(arch_prepare_kprobe);
+
+/* Install breakpoint in text */
+void arch_arm_kprobe(struct kprobe *p)
+{
+	*p->addr = breakpoint_insn;
+}
+NOKPROBE_SYMBOL(arch_arm_kprobe);
+
+/* Remove breakpoint from text */
+void arch_disarm_kprobe(struct kprobe *p)
+{
+	*p->addr = 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 = NULL;
+	}
+}
+NOKPROBE_SYMBOL(arch_remove_kprobe);
+
+static 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_irq = kcb->kprobe_saved_irq;
+	kcb->prev_kprobe.saved_era = 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 = kcb->prev_kprobe.status;
+	kcb->kprobe_saved_irq = kcb->prev_kprobe.saved_irq;
+	kcb->kprobe_saved_era = 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 = regs->csr_prmd & CSR_PRMD_PIE;
+	kcb->kprobe_saved_era = 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 = KPROBE_REENTER;
+	} else {
+		kcb->kprobe_status = KPROBE_HIT_SS;
+	}
+
+	if (p->ainsn.insn->word == breakpoint_insn.word) {
+		regs->csr_prmd &= ~CSR_PRMD_PIE;
+		regs->csr_prmd |= kcb->kprobe_saved_irq;
+		return;
+	}
+
+	regs->csr_prmd &= ~CSR_PRMD_PIE;
+
+	if (insns_are_not_simulated(p, regs)) {
+		kcb->kprobe_status = KPROBE_HIT_SS;
+		regs->csr_era = (unsigned long)&p->ainsn.insn[0];
+	} else {
+		kcb->kprobe_status = 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 = instruction_pointer(regs);
+
+	kcb = get_kprobe_ctlblk();
+	cur_kprobe = kprobe_running();
+
+	p = 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 = 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 bool kprobe_post_handler(struct pt_regs *regs)
+{
+	struct kprobe *cur = kprobe_running();
+	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+	if (!cur)
+		return false;
+
+	/* Restore back the original saved kprobes variables and continue */
+	if (kcb->kprobe_status == KPROBE_REENTER) {
+		restore_previous_kprobe(kcb);
+		return true;
+	}
+
+	/* Call post handler */
+	kcb->kprobe_status = KPROBE_HIT_SSDONE;
+	if (cur->post_handler)
+		cur->post_handler(cur, regs, 0);
+
+	regs->csr_era = kcb->kprobe_saved_era + LOONGARCH_INSN_SIZE;
+	regs->csr_prmd |= 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 = get_kprobe_ctlblk();
+
+	if (kcb->kprobe_status & KPROBE_HIT_SS) {
+		regs->csr_era = kcb->kprobe_saved_era + LOONGARCH_INSN_SIZE;
+		regs->csr_prmd |= 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 = (struct die_args *)data;
+	int ret = NOTIFY_DONE;
+
+	switch (val) {
+	case DIE_BREAK:
+		if (kprobe_pre_handler(args->regs))
+			ret = NOTIFY_STOP;
+		break;
+	case DIE_SSTEPBP:
+		if (kprobe_post_handler(args->regs))
+			ret = NOTIFY_STOP;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+NOKPROBE_SYMBOL(kprobe_exceptions_notify);
+
+/*
+ * Provide a blacklist of symbols identifying ranges which cannot be kprobed.
+ * This blacklist is exposed to userspace via debugfs (kprobes/blacklist).
+ */
+int __init arch_populate_kprobe_blacklist(void)
+{
+	return kprobe_add_area_blacklist((unsigned long)__irqentry_text_start,
+					 (unsigned long)__irqentry_text_end);
+}
+
+int __init arch_init_kprobes(void)
+{
+	return 0;
+}
diff --git a/arch/loongarch/mm/fault.c b/arch/loongarch/mm/fault.c
index 1ccd536..5882f17 100644
--- a/arch/loongarch/mm/fault.c
+++ b/arch/loongarch/mm/fault.c
@@ -253,6 +253,9 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
 {
 	irqentry_state_t state = irqentry_enter(regs);
 
+	if (kprobe_page_fault(regs, current->thread.trap_nr))
+		return;
+
 	/* Enable interrupt if enabled in parent context */
 	if (likely(regs->csr_prmd & CSR_PRMD_PIE))
 		local_irq_enable();
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2 3/5] LoongArch: Add kretprobe support
  2022-09-28  0:50 [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Tiezhu Yang
  2022-09-28  0:50 ` [PATCH v2 1/5] LoongArch: Simulate branch and PC instructions Tiezhu Yang
  2022-09-28  0:50 ` [PATCH v2 2/5] LoongArch: Add kprobe support Tiezhu Yang
@ 2022-09-28  0:50 ` Tiezhu Yang
  2022-11-14  4:43   ` Huacai Chen
  2022-09-28  0:50 ` [PATCH v2 4/5] samples/kprobes: Add LoongArch support Tiezhu Yang
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: Tiezhu Yang @ 2022-09-28  0:50 UTC (permalink / raw)
  To: Huacai Chen, Masami Hiramatsu; +Cc: loongarch, linux-kernel

Use the generic kretprobe trampoline handler to add kretprobe
support for LoongArch.

Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
---
 arch/loongarch/Kconfig                     |  1 +
 arch/loongarch/kernel/Makefile             |  2 +-
 arch/loongarch/kernel/kprobes.c            | 24 ++++++++
 arch/loongarch/kernel/kprobes_trampoline.S | 97 ++++++++++++++++++++++++++++++
 4 files changed, 123 insertions(+), 1 deletion(-)
 create mode 100644 arch/loongarch/kernel/kprobes_trampoline.S

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 8debd70..877be6a 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/Makefile b/arch/loongarch/kernel/Makefile
index ff98d8a..48f50607 100644
--- a/arch/loongarch/kernel/Makefile
+++ b/arch/loongarch/kernel/Makefile
@@ -33,6 +33,6 @@ obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o
 
 obj-$(CONFIG_PERF_EVENTS)	+= perf_event.o perf_regs.o
 
-obj-$(CONFIG_KPROBES)		+= kprobes.o
+obj-$(CONFIG_KPROBES)		+= kprobes.o kprobes_trampoline.o
 
 CPPFLAGS_vmlinux.lds		:= $(KBUILD_CFLAGS)
diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
index c11f6e0..ca3f1dc 100644
--- a/arch/loongarch/kernel/kprobes.c
+++ b/arch/loongarch/kernel/kprobes.c
@@ -306,6 +306,30 @@ int __init arch_populate_kprobe_blacklist(void)
 					 (unsigned long)__irqentry_text_end);
 }
 
+/* Called from __kretprobe_trampoline */
+void __used *trampoline_probe_handler(struct pt_regs *regs)
+{
+	return (void *)kretprobe_trampoline_handler(regs, NULL);
+}
+NOKPROBE_SYMBOL(trampoline_probe_handler);
+
+void arch_prepare_kretprobe(struct kretprobe_instance *ri,
+			    struct pt_regs *regs)
+{
+	ri->ret_addr = (kprobe_opcode_t *)regs->regs[1];
+	ri->fp = NULL;
+
+	/* Replace the return addr with trampoline addr */
+	regs->regs[1] = (unsigned long)&__kretprobe_trampoline;
+}
+NOKPROBE_SYMBOL(arch_prepare_kretprobe);
+
+int arch_trampoline_kprobe(struct kprobe *p)
+{
+	return 0;
+}
+NOKPROBE_SYMBOL(arch_trampoline_kprobe);
+
 int __init arch_init_kprobes(void)
 {
 	return 0;
diff --git a/arch/loongarch/kernel/kprobes_trampoline.S b/arch/loongarch/kernel/kprobes_trampoline.S
new file mode 100644
index 0000000..9888ab8
--- /dev/null
+++ b/arch/loongarch/kernel/kprobes_trampoline.S
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#include <linux/linkage.h>
+#include <asm/stackframe.h>
+
+	.text
+
+	.macro save_all_base_regs
+	cfi_st	zero, PT_R0
+	cfi_st	ra, PT_R1
+	cfi_st	tp, PT_R2
+	cfi_st	a0, PT_R4
+	cfi_st	a1, PT_R5
+	cfi_st	a2, PT_R6
+	cfi_st	a3, PT_R7
+	cfi_st	a4, PT_R8
+	cfi_st	a5, PT_R9
+	cfi_st	a6, PT_R10
+	cfi_st	a7, PT_R11
+	cfi_st	t0, PT_R12
+	cfi_st	t1, PT_R13
+	cfi_st	t2, PT_R14
+	cfi_st	t3, PT_R15
+	cfi_st	t4, PT_R16
+	cfi_st	t5, PT_R17
+	cfi_st	t6, PT_R18
+	cfi_st	t7, PT_R19
+	cfi_st	t8, PT_R20
+	cfi_st	u0, PT_R21
+	cfi_st	fp, PT_R22
+	cfi_st	s0, PT_R23
+	cfi_st	s1, PT_R24
+	cfi_st	s2, PT_R25
+	cfi_st	s3, PT_R26
+	cfi_st	s4, PT_R27
+	cfi_st	s5, PT_R28
+	cfi_st	s6, PT_R29
+	cfi_st	s7, PT_R30
+	cfi_st	s8, PT_R31
+	addi.d	t0, sp, PT_SIZE
+	LONG_S	t0, sp, PT_R3
+	csrrd	t0, LOONGARCH_CSR_CRMD
+	andi	t0, t0, 0x7 /* extract bit[1:0] PLV, bit[2] IE */
+	LONG_S	t0, sp, PT_PRMD
+	.endm
+
+	.macro restore_all_base_regs
+	cfi_ld	zero, PT_R0
+	cfi_ld	tp, PT_R2
+	cfi_ld	a0, PT_R4
+	cfi_ld	a1, PT_R5
+	cfi_ld	a2, PT_R6
+	cfi_ld	a3, PT_R7
+	cfi_ld	a4, PT_R8
+	cfi_ld	a5, PT_R9
+	cfi_ld	a6, PT_R10
+	cfi_ld	a7, PT_R11
+	cfi_ld	t0, PT_R12
+	cfi_ld	t1, PT_R13
+	cfi_ld	t2, PT_R14
+	cfi_ld	t3, PT_R15
+	cfi_ld	t4, PT_R16
+	cfi_ld	t5, PT_R17
+	cfi_ld	t6, PT_R18
+	cfi_ld	t7, PT_R19
+	cfi_ld	t8, PT_R20
+	cfi_ld	u0, PT_R21
+	cfi_ld	fp, PT_R22
+	cfi_ld	s0, PT_R23
+	cfi_ld	s1, PT_R24
+	cfi_ld	s2, PT_R25
+	cfi_ld	s3, PT_R26
+	cfi_ld	s4, PT_R27
+	cfi_ld	s5, PT_R28
+	cfi_ld	s6, PT_R29
+	cfi_ld	s7, PT_R30
+	cfi_ld	s8, PT_R31
+	LONG_L  t0, sp, PT_PRMD
+	li.d	t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */
+	csrxchg t0, t1, LOONGARCH_CSR_CRMD
+	.endm
+
+SYM_CODE_START(__kretprobe_trampoline)
+	addi.d	sp, sp, -PT_SIZE
+	save_all_base_regs
+
+	move a0, sp /* pt_regs */
+
+	bl trampoline_probe_handler
+
+	/* use the result as the return-address */
+	move ra, a0
+
+	restore_all_base_regs
+	addi.d	sp, sp, PT_SIZE
+
+	jr ra
+SYM_CODE_END(__kretprobe_trampoline)
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2 4/5] samples/kprobes: Add LoongArch support
  2022-09-28  0:50 [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Tiezhu Yang
                   ` (2 preceding siblings ...)
  2022-09-28  0:50 ` [PATCH v2 3/5] LoongArch: Add kretprobe support Tiezhu Yang
@ 2022-09-28  0:50 ` Tiezhu Yang
  2022-09-28  0:50 ` [PATCH v2 5/5] LoongArch: Enable KPROBES in default config Tiezhu Yang
  2022-11-14  4:31 ` [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Huacai Chen
  5 siblings, 0 replies; 16+ messages in thread
From: Tiezhu Yang @ 2022-09-28  0:50 UTC (permalink / raw)
  To: Huacai Chen, Masami Hiramatsu; +Cc: loongarch, linux-kernel

Add LoongArch specific info in handler_pre() and handler_post().

Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
---
 samples/kprobes/kprobe_example.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/samples/kprobes/kprobe_example.c b/samples/kprobes/kprobe_example.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, struct pt_regs *regs)
 	pr_info("<%s> p->addr, 0x%p, ip = 0x%lx, flags = 0x%lx\n",
 		p->symbol_name, p->addr, regs->psw.addr, regs->flags);
 #endif
+#ifdef CONFIG_LOONGARCH
+	pr_info("<%s> p->addr = 0x%p, era = 0x%lx, estat = 0x%lx\n",
+		p->symbol_name, p->addr, regs->csr_era, regs->csr_estat);
+#endif
 
 	/* A dump_stack() here will give a stack backtrace */
 	return 0;
@@ -92,6 +96,10 @@ static void __kprobes handler_post(struct kprobe *p, struct pt_regs *regs,
 	pr_info("<%s> p->addr, 0x%p, flags = 0x%lx\n",
 		p->symbol_name, p->addr, regs->flags);
 #endif
+#ifdef CONFIG_LOONGARCH
+	pr_info("<%s> p->addr = 0x%p, estat = 0x%lx\n",
+		p->symbol_name, p->addr, regs->csr_estat);
+#endif
 }
 
 static int __init kprobe_init(void)
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2 5/5] LoongArch: Enable KPROBES in default config
  2022-09-28  0:50 [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Tiezhu Yang
                   ` (3 preceding siblings ...)
  2022-09-28  0:50 ` [PATCH v2 4/5] samples/kprobes: Add LoongArch support Tiezhu Yang
@ 2022-09-28  0:50 ` Tiezhu Yang
  2022-11-14  4:31 ` [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Huacai Chen
  5 siblings, 0 replies; 16+ messages in thread
From: Tiezhu Yang @ 2022-09-28  0:50 UTC (permalink / raw)
  To: Huacai Chen, Masami Hiramatsu; +Cc: loongarch, linux-kernel

Kprobes for LoongArch is supported now, update loongson3_defconfig
to enable KPROBES.

Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
---
 arch/loongarch/configs/loongson3_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/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=y
 CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y
 CONFIG_EFI_CAPSULE_LOADER=m
 CONFIG_EFI_TEST=m
+CONFIG_KPROBES=y
 CONFIG_MODULES=y
 CONFIG_MODULE_FORCE_LOAD=y
 CONFIG_MODULE_UNLOAD=y
-- 
2.1.0


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch
  2022-09-28  0:50 [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Tiezhu Yang
                   ` (4 preceding siblings ...)
  2022-09-28  0:50 ` [PATCH v2 5/5] LoongArch: Enable KPROBES in default config Tiezhu Yang
@ 2022-11-14  4:31 ` Huacai Chen
  2022-11-15  8:44   ` Tiezhu Yang
  5 siblings, 1 reply; 16+ messages in thread
From: Huacai Chen @ 2022-11-14  4:31 UTC (permalink / raw)
  To: Tiezhu Yang; +Cc: Masami Hiramatsu, loongarch, linux-kernel

Hi, Tiezhu,

Thanks for your patches, however,
1, You should rebase your code, since some functions such as
is_pc_insn is provided in the github's loongarch-next branch;
2, Your code may have some problems about preemption, you can do
something like this commit [1];
3, I don't think Kprobe should be enabled by default, distribution
configs can enable it if needed.

[1] https://github.com/loongson/linux/commit/023d5412ab5c154b87d57d70d465a4243df53717

Huacai

On Wed, Sep 28, 2022 at 8:50 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>
> v2:
>   -- Split simu_branch() and simu_pc() into a single patch
>   -- Call kprobe_page_fault() in do_page_fault()
>   -- Add kprobes_trampoline.S for kretprobe
>
> Tiezhu Yang (5):
>   LoongArch: Simulate branch and PC instructions
>   LoongArch: Add kprobe support
>   LoongArch: Add kretprobe support
>   samples/kprobes: Add LoongArch support
>   LoongArch: Enable KPROBES in default config
>
>  arch/loongarch/Kconfig                     |   2 +
>  arch/loongarch/configs/loongson3_defconfig |   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               | 113 ++++++++++
>  arch/loongarch/kernel/kprobes.c            | 336 +++++++++++++++++++++++++++++
>  arch/loongarch/kernel/kprobes_trampoline.S |  97 +++++++++
>  arch/loongarch/mm/fault.c                  |   3 +
>  samples/kprobes/kprobe_example.c           |   8 +
>  11 files changed, 638 insertions(+)
>  create mode 100644 arch/loongarch/include/asm/kprobes.h
>  create mode 100644 arch/loongarch/kernel/kprobes.c
>  create mode 100644 arch/loongarch/kernel/kprobes_trampoline.S
>
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 1/5] LoongArch: Simulate branch and PC instructions
  2022-09-28  0:50 ` [PATCH v2 1/5] LoongArch: Simulate branch and PC instructions Tiezhu Yang
@ 2022-11-14  4:37   ` Huacai Chen
  0 siblings, 0 replies; 16+ messages in thread
From: Huacai Chen @ 2022-11-14  4:37 UTC (permalink / raw)
  To: Tiezhu Yang; +Cc: Masami Hiramatsu, loongarch, linux-kernel

Hi, Tiezhu,

On Wed, Sep 28, 2022 at 8:50 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>
> According to LoongArch Reference Manual, simulate branch and
> PC instructions, this is preparation for later patch.
>
> Link: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#branch-instructions
> Link: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_pcaddi_pcaddu121_pcaddu18l_pcalau12i
>
> Co-developed-by: Jinyang He <hejinyang@loongson.cn>
> Signed-off-by: Jinyang He <hejinyang@loongson.cn>
> Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
> ---
>  arch/loongarch/include/asm/inst.h   |  19 ++++++
>  arch/loongarch/include/asm/ptrace.h |   1 +
>  arch/loongarch/kernel/inst.c        | 113 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 133 insertions(+)
>
> diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
> index fce1843..93d5cd4 100644
> --- a/arch/loongarch/include/asm/inst.h
> +++ b/arch/loongarch/include/asm/inst.h
> @@ -7,6 +7,7 @@
>
>  #include <linux/types.h>
>  #include <asm/asm.h>
> +#include <asm/ptrace.h>
>
>  #define INSN_BREAK             0x002a0000
>
> @@ -28,6 +29,8 @@ enum reg0i26_op {
>  enum reg1i20_op {
>         lu12iw_op       = 0x0a,
>         lu32id_op       = 0x0b,
> +       pcaddi_op       = 0x0c,
> +       pcalau12i_op    = 0x0d,
>         pcaddu12i_op    = 0x0e,
>         pcaddu18i_op    = 0x0f,
>  };
> @@ -313,6 +316,12 @@ static inline bool is_branch_ins(union loongarch_instruction *ip)
>                 ip->reg1i21_format.opcode <= bgeu_op;
>  }
>
> +static inline bool is_pc_ins(union loongarch_instruction *ip)
> +{
> +       return ip->reg1i20_format.opcode >= pcaddi_op &&
> +               ip->reg1i20_format.opcode <= pcaddu18i_op;
> +}
> +
>  static inline bool is_ra_save_ins(union loongarch_instruction *ip)
>  {
>         /* st.d $ra, $sp, offset */
> @@ -334,6 +343,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, int imm);
>  u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned 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;
> +}
>
>  static inline bool signed_imm_check(long val, unsigned int bit)
>  {
> diff --git a/arch/loongarch/include/asm/ptrace.h b/arch/loongarch/include/asm/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
>
>  #include <asm/page.h>
> +#include <asm/irqflags.h>
>  #include <asm/thread_info.h>
>  #include <uapi/asm/ptrace.h>
>
> diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c
> index b1df0ec..f5c1eff6 100644
> --- a/arch/loongarch/kernel/inst.c
> +++ b/arch/loongarch/kernel/inst.c
> @@ -38,3 +38,116 @@ u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned l
>
>         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 = regs->csr_era;
> +
In previous versions we checked the alignment of era in simu_branch()
and simu_pc(), now they are unnecessary?

Huacai
> +       imm_l = insn.reg0i26_format.immediate_l;
> +       imm_h = insn.reg0i26_format.immediate_h;
> +       switch (insn.reg0i26_format.opcode) {
> +       case b_op:
> +               regs->csr_era = pc + sign_extended((imm_h << 16 | imm_l) << 2, 27);
> +               return;
> +       case bl_op:
> +               regs->csr_era = pc + sign_extended((imm_h << 16 | imm_l) << 2, 27);
> +               regs->regs[1] = pc + LOONGARCH_INSN_SIZE;
> +               return;
> +       }
> +
> +       imm_l = insn.reg1i21_format.immediate_l;
> +       imm_h = insn.reg1i21_format.immediate_h;
> +       rj = insn.reg1i21_format.rj;
> +       switch (insn.reg1i21_format.opcode) {
> +       case beqz_op:
> +               if (regs->regs[rj] == 0)
> +                       regs->csr_era = pc + sign_extended((imm_h << 16 | imm_l) << 2, 22);
> +               else
> +                       regs->csr_era = pc + LOONGARCH_INSN_SIZE;
> +               return;
> +       case bnez_op:
> +               if (regs->regs[rj] != 0)
> +                       regs->csr_era = pc + sign_extended((imm_h << 16 | imm_l) << 2, 22);
> +               else
> +                       regs->csr_era = pc + LOONGARCH_INSN_SIZE;
> +               return;
> +       }
> +
> +       imm = insn.reg2i16_format.immediate;
> +       rj = insn.reg2i16_format.rj;
> +       rd = insn.reg2i16_format.rd;
> +       switch (insn.reg2i16_format.opcode) {
> +       case beq_op:
> +               if (regs->regs[rj] == regs->regs[rd])
> +                       regs->csr_era = pc + sign_extended(imm << 2, 17);
> +               else
> +                       regs->csr_era = pc + LOONGARCH_INSN_SIZE;
> +               break;
> +       case bne_op:
> +               if (regs->regs[rj] != regs->regs[rd])
> +                       regs->csr_era = pc + sign_extended(imm << 2, 17);
> +               else
> +                       regs->csr_era = pc + LOONGARCH_INSN_SIZE;
> +               break;
> +       case blt_op:
> +               if ((long)regs->regs[rj] < (long)regs->regs[rd])
> +                       regs->csr_era = pc + sign_extended(imm << 2, 17);
> +               else
> +                       regs->csr_era = pc + LOONGARCH_INSN_SIZE;
> +               break;
> +       case bge_op:
> +               if ((long)regs->regs[rj] >= (long)regs->regs[rd])
> +                       regs->csr_era = pc + sign_extended(imm << 2, 17);
> +               else
> +                       regs->csr_era = pc + LOONGARCH_INSN_SIZE;
> +               break;
> +       case bltu_op:
> +               if (regs->regs[rj] < regs->regs[rd])
> +                       regs->csr_era = pc + sign_extended(imm << 2, 17);
> +               else
> +                       regs->csr_era = pc + LOONGARCH_INSN_SIZE;
> +               break;
> +       case bgeu_op:
> +               if (regs->regs[rj] >= regs->regs[rd])
> +                       regs->csr_era = pc + sign_extended(imm << 2, 17);
> +               else
> +                       regs->csr_era = pc + LOONGARCH_INSN_SIZE;
> +               break;
> +       case jirl_op:
> +               regs->csr_era = regs->regs[rj] + sign_extended(imm << 2, 17);
> +               regs->regs[rd] = pc + LOONGARCH_INSN_SIZE;
> +               break;
> +       default:
> +               pr_info("%s: unknown opcode\n", __func__);
> +               return;
> +       }
> +}
> +
> +void simu_pc(struct pt_regs *regs, union loongarch_instruction insn)
> +{
> +       unsigned long pc = regs->csr_era;
> +       unsigned int rd = insn.reg1i20_format.rd;
> +       unsigned int imm = insn.reg1i20_format.immediate;
> +
> +       switch (insn.reg1i20_format.opcode) {
> +       case pcaddi_op:
> +               regs->regs[rd] = pc + sign_extended(imm << 2, 21);
> +               break;
> +       case pcaddu12i_op:
> +               regs->regs[rd] = pc + sign_extended(imm << 12, 31);
> +               break;
> +       case pcaddu18i_op:
> +               regs->regs[rd] = pc + sign_extended(imm << 18, 37);
> +               break;
> +       case pcalau12i_op:
> +               regs->regs[rd] = pc + sign_extended(imm << 12, 31);
> +               regs->regs[rd] &= ~((1 << 12) - 1);
> +               break;
> +       default:
> +               pr_info("%s: unknown opcode\n", __func__);
> +               return;
> +       }
> +
> +       regs->csr_era += LOONGARCH_INSN_SIZE;
> +}
> --
> 2.1.0
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 2/5] LoongArch: Add kprobe support
  2022-09-28  0:50 ` [PATCH v2 2/5] LoongArch: Add kprobe support Tiezhu Yang
@ 2022-11-14  4:41   ` Huacai Chen
  0 siblings, 0 replies; 16+ messages in thread
From: Huacai Chen @ 2022-11-14  4:41 UTC (permalink / raw)
  To: Tiezhu Yang; +Cc: Masami Hiramatsu, loongarch, linux-kernel

Hi, Tiezhu,

On Wed, Sep 28, 2022 at 8:50 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>
> 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 <yangtiezhu@loongson.cn>
> ---
>  arch/loongarch/Kconfig               |   1 +
>  arch/loongarch/include/asm/inst.h    |  12 ++
>  arch/loongarch/include/asm/kprobes.h |  44 +++++
>  arch/loongarch/kernel/Makefile       |   2 +
>  arch/loongarch/kernel/kprobes.c      | 312 +++++++++++++++++++++++++++++++++++
>  arch/loongarch/mm/fault.c            |   3 +
>  6 files changed, 374 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 be8389f..8debd70 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 93d5cd4..fc8879b 100644
> --- a/arch/loongarch/include/asm/inst.h
> +++ b/arch/loongarch/include/asm/inst.h
> @@ -21,6 +21,10 @@
>
>  #define ADDR_IMM(addr, INSN)   ((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN)
>
> +enum reg0i15_op {
> +       break_op        = 0x54,
> +};
> +
>  enum reg0i26_op {
>         b_op            = 0x14,
>         bl_op           = 0x15,
> @@ -38,6 +42,8 @@ enum reg1i20_op {
>  enum reg1i21_op {
>         beqz_op         = 0x10,
>         bnez_op         = 0x11,
> +       bceqz_op        = 0x48000000,
> +       bcnez_op        = 0x48000100,
>  };
>
>  enum reg2_op {
> @@ -167,6 +173,11 @@ enum reg3sa2_op {
>         alsld_op        = 0x16,
>  };
>
> +struct reg0i15_format {
> +       unsigned int immediate : 15;
> +       unsigned int opcode : 17;
> +};
> +
>  struct reg0i26_format {
>         unsigned int immediate_h : 10;
>         unsigned int immediate_l : 16;
> @@ -252,6 +263,7 @@ struct reg3sa2_format {
>
>  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;
> 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 <asm-generic/kprobes.h>
> +
> +#ifdef CONFIG_KPROBES
> +
> +#include <asm/inst.h>
> +
> +#define __ARCH_WANT_KPROBES_INSN_SLOT
> +#define MAX_INSN_SIZE                  2
> +
> +#define flush_insn_slot(p)             do { } while (0)
We don't need an ibar here?

> +#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/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) += unwind_prologue.o
>
>  obj-$(CONFIG_PERF_EVENTS)      += perf_event.o perf_regs.o
>
> +obj-$(CONFIG_KPROBES)          += kprobes.o
> +
>  CPPFLAGS_vmlinux.lds           := $(KBUILD_CFLAGS)
> diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
> new file mode 100644
> index 0000000..c11f6e0
> --- /dev/null
> +++ b/arch/loongarch/kernel/kprobes.c
> @@ -0,0 +1,312 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <linux/kprobes.h>
> +#include <linux/kdebug.h>
> +#include <asm/break.h>
> +
> +static const union loongarch_instruction breakpoint_insn = {
> +       .reg0i15_format = {
> +               .opcode = break_op,
> +               .immediate = BRK_KPROBE_BP,
> +       }
> +};
> +
> +static const union loongarch_instruction singlestep_insn = {
> +       .reg0i15_format = {
> +               .opcode = break_op,
> +               .immediate = 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 = p->addr[0];
> +       if (insns_are_not_supported(insn))
> +               return -EINVAL;
> +
> +       p->ainsn.insn = get_insn_slot();
> +       if (!p->ainsn.insn)
> +               return -ENOMEM;
> +
> +       p->ainsn.insn[0] = *p->addr;
> +       p->ainsn.insn[1] = singlestep_insn;
> +
> +       p->opcode = *p->addr;
> +
> +       return 0;
> +}
> +NOKPROBE_SYMBOL(arch_prepare_kprobe);
> +
> +/* Install breakpoint in text */
> +void arch_arm_kprobe(struct kprobe *p)
> +{
> +       *p->addr = breakpoint_insn;
> +}
> +NOKPROBE_SYMBOL(arch_arm_kprobe);
> +
> +/* Remove breakpoint from text */
> +void arch_disarm_kprobe(struct kprobe *p)
> +{
> +       *p->addr = 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 = NULL;
> +       }
> +}
> +NOKPROBE_SYMBOL(arch_remove_kprobe);
> +
> +static 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_irq = kcb->kprobe_saved_irq;
> +       kcb->prev_kprobe.saved_era = 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 = kcb->prev_kprobe.status;
> +       kcb->kprobe_saved_irq = kcb->prev_kprobe.saved_irq;
> +       kcb->kprobe_saved_era = 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 = regs->csr_prmd & CSR_PRMD_PIE;
> +       kcb->kprobe_saved_era = 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 = KPROBE_REENTER;
> +       } else {
> +               kcb->kprobe_status = KPROBE_HIT_SS;
> +       }
> +
> +       if (p->ainsn.insn->word == breakpoint_insn.word) {
> +               regs->csr_prmd &= ~CSR_PRMD_PIE;
> +               regs->csr_prmd |= kcb->kprobe_saved_irq;
> +               return;
> +       }
> +
> +       regs->csr_prmd &= ~CSR_PRMD_PIE;
> +
> +       if (insns_are_not_simulated(p, regs)) {
> +               kcb->kprobe_status = KPROBE_HIT_SS;
> +               regs->csr_era = (unsigned long)&p->ainsn.insn[0];
> +       } else {
> +               kcb->kprobe_status = 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 = instruction_pointer(regs);
> +
> +       kcb = get_kprobe_ctlblk();
> +       cur_kprobe = kprobe_running();
> +
> +       p = 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 = 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 bool kprobe_post_handler(struct pt_regs *regs)
> +{
> +       struct kprobe *cur = kprobe_running();
> +       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
> +
> +       if (!cur)
> +               return false;
> +
> +       /* Restore back the original saved kprobes variables and continue */
> +       if (kcb->kprobe_status == KPROBE_REENTER) {
> +               restore_previous_kprobe(kcb);
> +               return true;
> +       }
> +
> +       /* Call post handler */
> +       kcb->kprobe_status = KPROBE_HIT_SSDONE;
> +       if (cur->post_handler)
> +               cur->post_handler(cur, regs, 0);
> +
> +       regs->csr_era = kcb->kprobe_saved_era + LOONGARCH_INSN_SIZE;
> +       regs->csr_prmd |= kcb->kprobe_saved_irq;
> +
> +       reset_current_kprobe();
> +
> +       return true;
> +}
> +NOKPROBE_SYMBOL(kprobe_post_handler);
I'm not sure whether kprobe_pre_handler/kprobe_post_handler is
suitable, but nearly all other architectures use
kprobe_handler/post_kprobe_handler.

Huacai
> +
> +bool kprobe_fault_handler(struct pt_regs *regs, int trapnr)
> +{
> +       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
> +
> +       if (kcb->kprobe_status & KPROBE_HIT_SS) {
> +               regs->csr_era = kcb->kprobe_saved_era + LOONGARCH_INSN_SIZE;
> +               regs->csr_prmd |= 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 = (struct die_args *)data;
> +       int ret = NOTIFY_DONE;
> +
> +       switch (val) {
> +       case DIE_BREAK:
> +               if (kprobe_pre_handler(args->regs))
> +                       ret = NOTIFY_STOP;
> +               break;
> +       case DIE_SSTEPBP:
> +               if (kprobe_post_handler(args->regs))
> +                       ret = NOTIFY_STOP;
> +               break;
> +       default:
> +               break;
> +       }
> +
> +       return ret;
> +}
> +NOKPROBE_SYMBOL(kprobe_exceptions_notify);
> +
> +/*
> + * Provide a blacklist of symbols identifying ranges which cannot be kprobed.
> + * This blacklist is exposed to userspace via debugfs (kprobes/blacklist).
> + */
> +int __init arch_populate_kprobe_blacklist(void)
> +{
> +       return kprobe_add_area_blacklist((unsigned long)__irqentry_text_start,
> +                                        (unsigned long)__irqentry_text_end);
> +}
> +
> +int __init arch_init_kprobes(void)
> +{
> +       return 0;
> +}
> diff --git a/arch/loongarch/mm/fault.c b/arch/loongarch/mm/fault.c
> index 1ccd536..5882f17 100644
> --- a/arch/loongarch/mm/fault.c
> +++ b/arch/loongarch/mm/fault.c
> @@ -253,6 +253,9 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
>  {
>         irqentry_state_t state = irqentry_enter(regs);
>
> +       if (kprobe_page_fault(regs, current->thread.trap_nr))
> +               return;
> +
>         /* Enable interrupt if enabled in parent context */
>         if (likely(regs->csr_prmd & CSR_PRMD_PIE))
>                 local_irq_enable();
> --
> 2.1.0
>
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 3/5] LoongArch: Add kretprobe support
  2022-09-28  0:50 ` [PATCH v2 3/5] LoongArch: Add kretprobe support Tiezhu Yang
@ 2022-11-14  4:43   ` Huacai Chen
  2022-11-14  6:11     ` Jinyang He
  0 siblings, 1 reply; 16+ messages in thread
From: Huacai Chen @ 2022-11-14  4:43 UTC (permalink / raw)
  To: Tiezhu Yang; +Cc: Masami Hiramatsu, loongarch, linux-kernel

Hi, Tiezhu and Jinyang,

On Wed, Sep 28, 2022 at 8:50 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>
> Use the generic kretprobe trampoline handler to add kretprobe
> support for LoongArch.
>
> Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
> ---
>  arch/loongarch/Kconfig                     |  1 +
>  arch/loongarch/kernel/Makefile             |  2 +-
>  arch/loongarch/kernel/kprobes.c            | 24 ++++++++
>  arch/loongarch/kernel/kprobes_trampoline.S | 97 ++++++++++++++++++++++++++++++
>  4 files changed, 123 insertions(+), 1 deletion(-)
>  create mode 100644 arch/loongarch/kernel/kprobes_trampoline.S
>
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> index 8debd70..877be6a 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/Makefile b/arch/loongarch/kernel/Makefile
> index ff98d8a..48f50607 100644
> --- a/arch/loongarch/kernel/Makefile
> +++ b/arch/loongarch/kernel/Makefile
> @@ -33,6 +33,6 @@ obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o
>
>  obj-$(CONFIG_PERF_EVENTS)      += perf_event.o perf_regs.o
>
> -obj-$(CONFIG_KPROBES)          += kprobes.o
> +obj-$(CONFIG_KPROBES)          += kprobes.o kprobes_trampoline.o
>
>  CPPFLAGS_vmlinux.lds           := $(KBUILD_CFLAGS)
> diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
> index c11f6e0..ca3f1dc 100644
> --- a/arch/loongarch/kernel/kprobes.c
> +++ b/arch/loongarch/kernel/kprobes.c
> @@ -306,6 +306,30 @@ int __init arch_populate_kprobe_blacklist(void)
>                                          (unsigned long)__irqentry_text_end);
>  }
>
> +/* Called from __kretprobe_trampoline */
> +void __used *trampoline_probe_handler(struct pt_regs *regs)
> +{
> +       return (void *)kretprobe_trampoline_handler(regs, NULL);
> +}
> +NOKPROBE_SYMBOL(trampoline_probe_handler);
> +
> +void arch_prepare_kretprobe(struct kretprobe_instance *ri,
> +                           struct pt_regs *regs)
> +{
> +       ri->ret_addr = (kprobe_opcode_t *)regs->regs[1];
> +       ri->fp = NULL;
> +
> +       /* Replace the return addr with trampoline addr */
> +       regs->regs[1] = (unsigned long)&__kretprobe_trampoline;
> +}
> +NOKPROBE_SYMBOL(arch_prepare_kretprobe);
> +
> +int arch_trampoline_kprobe(struct kprobe *p)
> +{
> +       return 0;
> +}
> +NOKPROBE_SYMBOL(arch_trampoline_kprobe);
> +
>  int __init arch_init_kprobes(void)
>  {
>         return 0;
> diff --git a/arch/loongarch/kernel/kprobes_trampoline.S b/arch/loongarch/kernel/kprobes_trampoline.S
> new file mode 100644
> index 0000000..9888ab8
> --- /dev/null
> +++ b/arch/loongarch/kernel/kprobes_trampoline.S
> @@ -0,0 +1,97 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +#include <linux/linkage.h>
> +#include <asm/stackframe.h>
> +
> +       .text
> +
> +       .macro save_all_base_regs
> +       cfi_st  zero, PT_R0
> +       cfi_st  ra, PT_R1
> +       cfi_st  tp, PT_R2
> +       cfi_st  a0, PT_R4
> +       cfi_st  a1, PT_R5
> +       cfi_st  a2, PT_R6
> +       cfi_st  a3, PT_R7
> +       cfi_st  a4, PT_R8
> +       cfi_st  a5, PT_R9
> +       cfi_st  a6, PT_R10
> +       cfi_st  a7, PT_R11
> +       cfi_st  t0, PT_R12
> +       cfi_st  t1, PT_R13
> +       cfi_st  t2, PT_R14
> +       cfi_st  t3, PT_R15
> +       cfi_st  t4, PT_R16
> +       cfi_st  t5, PT_R17
> +       cfi_st  t6, PT_R18
> +       cfi_st  t7, PT_R19
> +       cfi_st  t8, PT_R20
> +       cfi_st  u0, PT_R21
> +       cfi_st  fp, PT_R22
> +       cfi_st  s0, PT_R23
> +       cfi_st  s1, PT_R24
> +       cfi_st  s2, PT_R25
> +       cfi_st  s3, PT_R26
> +       cfi_st  s4, PT_R27
> +       cfi_st  s5, PT_R28
> +       cfi_st  s6, PT_R29
> +       cfi_st  s7, PT_R30
> +       cfi_st  s8, PT_R31
> +       addi.d  t0, sp, PT_SIZE
> +       LONG_S  t0, sp, PT_R3
> +       csrrd   t0, LOONGARCH_CSR_CRMD
> +       andi    t0, t0, 0x7 /* extract bit[1:0] PLV, bit[2] IE */
> +       LONG_S  t0, sp, PT_PRMD
> +       .endm
> +
> +       .macro restore_all_base_regs
> +       cfi_ld  zero, PT_R0
> +       cfi_ld  tp, PT_R2
> +       cfi_ld  a0, PT_R4
> +       cfi_ld  a1, PT_R5
> +       cfi_ld  a2, PT_R6
> +       cfi_ld  a3, PT_R7
> +       cfi_ld  a4, PT_R8
> +       cfi_ld  a5, PT_R9
> +       cfi_ld  a6, PT_R10
> +       cfi_ld  a7, PT_R11
> +       cfi_ld  t0, PT_R12
> +       cfi_ld  t1, PT_R13
> +       cfi_ld  t2, PT_R14
> +       cfi_ld  t3, PT_R15
> +       cfi_ld  t4, PT_R16
> +       cfi_ld  t5, PT_R17
> +       cfi_ld  t6, PT_R18
> +       cfi_ld  t7, PT_R19
> +       cfi_ld  t8, PT_R20
> +       cfi_ld  u0, PT_R21
> +       cfi_ld  fp, PT_R22
> +       cfi_ld  s0, PT_R23
> +       cfi_ld  s1, PT_R24
> +       cfi_ld  s2, PT_R25
> +       cfi_ld  s3, PT_R26
> +       cfi_ld  s4, PT_R27
> +       cfi_ld  s5, PT_R28
> +       cfi_ld  s6, PT_R29
> +       cfi_ld  s7, PT_R30
> +       cfi_ld  s8, PT_R31
> +       LONG_L  t0, sp, PT_PRMD
> +       li.d    t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */
> +       csrxchg t0, t1, LOONGARCH_CSR_CRMD
> +       .endm
Do you think we need to save and restore all regs here?

Huacai
> +
> +SYM_CODE_START(__kretprobe_trampoline)
> +       addi.d  sp, sp, -PT_SIZE
> +       save_all_base_regs
> +
> +       move a0, sp /* pt_regs */
> +
> +       bl trampoline_probe_handler
> +
> +       /* use the result as the return-address */
> +       move ra, a0
> +
> +       restore_all_base_regs
> +       addi.d  sp, sp, PT_SIZE
> +
> +       jr ra
> +SYM_CODE_END(__kretprobe_trampoline)
> --
> 2.1.0
>
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 3/5] LoongArch: Add kretprobe support
  2022-11-14  4:43   ` Huacai Chen
@ 2022-11-14  6:11     ` Jinyang He
  2022-11-14  6:50       ` Huacai Chen
  0 siblings, 1 reply; 16+ messages in thread
From: Jinyang He @ 2022-11-14  6:11 UTC (permalink / raw)
  To: Huacai Chen, Tiezhu Yang; +Cc: Masami Hiramatsu, loongarch, linux-kernel

On 2022/11/14 下午12:43, Huacai Chen wrote:

> Hi, Tiezhu and Jinyang,
>
> On Wed, Sep 28, 2022 at 8:50 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>> Use the generic kretprobe trampoline handler to add kretprobe
>> support for LoongArch.
>>
>> Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
>> ---
>>   arch/loongarch/Kconfig                     |  1 +
>>   arch/loongarch/kernel/Makefile             |  2 +-
>>   arch/loongarch/kernel/kprobes.c            | 24 ++++++++
>>   arch/loongarch/kernel/kprobes_trampoline.S | 97 ++++++++++++++++++++++++++++++
>>   4 files changed, 123 insertions(+), 1 deletion(-)
>>   create mode 100644 arch/loongarch/kernel/kprobes_trampoline.S
>>
>> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
>> index 8debd70..877be6a 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/Makefile b/arch/loongarch/kernel/Makefile
>> index ff98d8a..48f50607 100644
>> --- a/arch/loongarch/kernel/Makefile
>> +++ b/arch/loongarch/kernel/Makefile
>> @@ -33,6 +33,6 @@ obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o
>>
>>   obj-$(CONFIG_PERF_EVENTS)      += perf_event.o perf_regs.o
>>
>> -obj-$(CONFIG_KPROBES)          += kprobes.o
>> +obj-$(CONFIG_KPROBES)          += kprobes.o kprobes_trampoline.o
>>
>>   CPPFLAGS_vmlinux.lds           := $(KBUILD_CFLAGS)
>> diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
>> index c11f6e0..ca3f1dc 100644
>> --- a/arch/loongarch/kernel/kprobes.c
>> +++ b/arch/loongarch/kernel/kprobes.c
>> @@ -306,6 +306,30 @@ int __init arch_populate_kprobe_blacklist(void)
>>                                           (unsigned long)__irqentry_text_end);
>>   }
>>
>> +/* Called from __kretprobe_trampoline */
>> +void __used *trampoline_probe_handler(struct pt_regs *regs)
>> +{
>> +       return (void *)kretprobe_trampoline_handler(regs, NULL);
>> +}
>> +NOKPROBE_SYMBOL(trampoline_probe_handler);
>> +
>> +void arch_prepare_kretprobe(struct kretprobe_instance *ri,
>> +                           struct pt_regs *regs)
>> +{
>> +       ri->ret_addr = (kprobe_opcode_t *)regs->regs[1];
>> +       ri->fp = NULL;
>> +
>> +       /* Replace the return addr with trampoline addr */
>> +       regs->regs[1] = (unsigned long)&__kretprobe_trampoline;
>> +}
>> +NOKPROBE_SYMBOL(arch_prepare_kretprobe);
>> +
>> +int arch_trampoline_kprobe(struct kprobe *p)
>> +{
>> +       return 0;
>> +}
>> +NOKPROBE_SYMBOL(arch_trampoline_kprobe);
>> +
>>   int __init arch_init_kprobes(void)
>>   {
>>          return 0;
>> diff --git a/arch/loongarch/kernel/kprobes_trampoline.S b/arch/loongarch/kernel/kprobes_trampoline.S
>> new file mode 100644
>> index 0000000..9888ab8
>> --- /dev/null
>> +++ b/arch/loongarch/kernel/kprobes_trampoline.S
>> @@ -0,0 +1,97 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +#include <linux/linkage.h>
>> +#include <asm/stackframe.h>
>> +
>> +       .text
>> +
>> +       .macro save_all_base_regs
>> +       cfi_st  zero, PT_R0
>> +       cfi_st  ra, PT_R1
>> +       cfi_st  tp, PT_R2
>> +       cfi_st  a0, PT_R4
>> +       cfi_st  a1, PT_R5
>> +       cfi_st  a2, PT_R6
>> +       cfi_st  a3, PT_R7
>> +       cfi_st  a4, PT_R8
>> +       cfi_st  a5, PT_R9
>> +       cfi_st  a6, PT_R10
>> +       cfi_st  a7, PT_R11
>> +       cfi_st  t0, PT_R12
>> +       cfi_st  t1, PT_R13
>> +       cfi_st  t2, PT_R14
>> +       cfi_st  t3, PT_R15
>> +       cfi_st  t4, PT_R16
>> +       cfi_st  t5, PT_R17
>> +       cfi_st  t6, PT_R18
>> +       cfi_st  t7, PT_R19
>> +       cfi_st  t8, PT_R20
>> +       cfi_st  u0, PT_R21
>> +       cfi_st  fp, PT_R22
>> +       cfi_st  s0, PT_R23
>> +       cfi_st  s1, PT_R24
>> +       cfi_st  s2, PT_R25
>> +       cfi_st  s3, PT_R26
>> +       cfi_st  s4, PT_R27
>> +       cfi_st  s5, PT_R28
>> +       cfi_st  s6, PT_R29
>> +       cfi_st  s7, PT_R30
>> +       cfi_st  s8, PT_R31
>> +       addi.d  t0, sp, PT_SIZE
>> +       LONG_S  t0, sp, PT_R3
>> +       csrrd   t0, LOONGARCH_CSR_CRMD
>> +       andi    t0, t0, 0x7 /* extract bit[1:0] PLV, bit[2] IE */
>> +       LONG_S  t0, sp, PT_PRMD
>> +       .endm
>> +
>> +       .macro restore_all_base_regs
>> +       cfi_ld  zero, PT_R0
>> +       cfi_ld  tp, PT_R2
>> +       cfi_ld  a0, PT_R4
>> +       cfi_ld  a1, PT_R5
>> +       cfi_ld  a2, PT_R6
>> +       cfi_ld  a3, PT_R7
>> +       cfi_ld  a4, PT_R8
>> +       cfi_ld  a5, PT_R9
>> +       cfi_ld  a6, PT_R10
>> +       cfi_ld  a7, PT_R11
>> +       cfi_ld  t0, PT_R12
>> +       cfi_ld  t1, PT_R13
>> +       cfi_ld  t2, PT_R14
>> +       cfi_ld  t3, PT_R15
>> +       cfi_ld  t4, PT_R16
>> +       cfi_ld  t5, PT_R17
>> +       cfi_ld  t6, PT_R18
>> +       cfi_ld  t7, PT_R19
>> +       cfi_ld  t8, PT_R20
>> +       cfi_ld  u0, PT_R21
>> +       cfi_ld  fp, PT_R22
>> +       cfi_ld  s0, PT_R23
>> +       cfi_ld  s1, PT_R24
>> +       cfi_ld  s2, PT_R25
>> +       cfi_ld  s3, PT_R26
>> +       cfi_ld  s4, PT_R27
>> +       cfi_ld  s5, PT_R28
>> +       cfi_ld  s6, PT_R29
>> +       cfi_ld  s7, PT_R30
>> +       cfi_ld  s8, PT_R31
>> +       LONG_L  t0, sp, PT_PRMD
>> +       li.d    t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */
>> +       csrxchg t0, t1, LOONGARCH_CSR_CRMD
>> +       .endm
> Do you think we need to save and restore all regs here?
>
> Huacai

Hi, Huacai,


Note that it is not function context. In the original kprobe design, it is
triggered by 'break' and then trap into exception with all pt_regs saved.
The all pt_regs will be visible to the user. So I think in this version
we should also support all regs to user. BTW, due to all exceptions is
trapped by 'break' something in pt_regs is not needed, like estat,
badvaddr and so on.


Thanks,

Jinyang

>> +
>> +SYM_CODE_START(__kretprobe_trampoline)
>> +       addi.d  sp, sp, -PT_SIZE
>> +       save_all_base_regs
>> +
>> +       move a0, sp /* pt_regs */
>> +
>> +       bl trampoline_probe_handler
>> +
>> +       /* use the result as the return-address */
>> +       move ra, a0
>> +
>> +       restore_all_base_regs
>> +       addi.d  sp, sp, PT_SIZE
>> +
>> +       jr ra
>> +SYM_CODE_END(__kretprobe_trampoline)
>> --
>> 2.1.0
>>
>>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 3/5] LoongArch: Add kretprobe support
  2022-11-14  6:11     ` Jinyang He
@ 2022-11-14  6:50       ` Huacai Chen
  2022-11-14  8:32         ` Jinyang He
  0 siblings, 1 reply; 16+ messages in thread
From: Huacai Chen @ 2022-11-14  6:50 UTC (permalink / raw)
  To: Jinyang He; +Cc: Tiezhu Yang, Masami Hiramatsu, loongarch, linux-kernel

On Mon, Nov 14, 2022 at 2:11 PM Jinyang He <hejinyang@loongson.cn> wrote:
>
> On 2022/11/14 下午12:43, Huacai Chen wrote:
>
> > Hi, Tiezhu and Jinyang,
> >
> > On Wed, Sep 28, 2022 at 8:50 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
> >> Use the generic kretprobe trampoline handler to add kretprobe
> >> support for LoongArch.
> >>
> >> Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
> >> ---
> >>   arch/loongarch/Kconfig                     |  1 +
> >>   arch/loongarch/kernel/Makefile             |  2 +-
> >>   arch/loongarch/kernel/kprobes.c            | 24 ++++++++
> >>   arch/loongarch/kernel/kprobes_trampoline.S | 97 ++++++++++++++++++++++++++++++
> >>   4 files changed, 123 insertions(+), 1 deletion(-)
> >>   create mode 100644 arch/loongarch/kernel/kprobes_trampoline.S
> >>
> >> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> >> index 8debd70..877be6a 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/Makefile b/arch/loongarch/kernel/Makefile
> >> index ff98d8a..48f50607 100644
> >> --- a/arch/loongarch/kernel/Makefile
> >> +++ b/arch/loongarch/kernel/Makefile
> >> @@ -33,6 +33,6 @@ obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o
> >>
> >>   obj-$(CONFIG_PERF_EVENTS)      += perf_event.o perf_regs.o
> >>
> >> -obj-$(CONFIG_KPROBES)          += kprobes.o
> >> +obj-$(CONFIG_KPROBES)          += kprobes.o kprobes_trampoline.o
> >>
> >>   CPPFLAGS_vmlinux.lds           := $(KBUILD_CFLAGS)
> >> diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
> >> index c11f6e0..ca3f1dc 100644
> >> --- a/arch/loongarch/kernel/kprobes.c
> >> +++ b/arch/loongarch/kernel/kprobes.c
> >> @@ -306,6 +306,30 @@ int __init arch_populate_kprobe_blacklist(void)
> >>                                           (unsigned long)__irqentry_text_end);
> >>   }
> >>
> >> +/* Called from __kretprobe_trampoline */
> >> +void __used *trampoline_probe_handler(struct pt_regs *regs)
> >> +{
> >> +       return (void *)kretprobe_trampoline_handler(regs, NULL);
> >> +}
> >> +NOKPROBE_SYMBOL(trampoline_probe_handler);
> >> +
> >> +void arch_prepare_kretprobe(struct kretprobe_instance *ri,
> >> +                           struct pt_regs *regs)
> >> +{
> >> +       ri->ret_addr = (kprobe_opcode_t *)regs->regs[1];
> >> +       ri->fp = NULL;
> >> +
> >> +       /* Replace the return addr with trampoline addr */
> >> +       regs->regs[1] = (unsigned long)&__kretprobe_trampoline;
> >> +}
> >> +NOKPROBE_SYMBOL(arch_prepare_kretprobe);
> >> +
> >> +int arch_trampoline_kprobe(struct kprobe *p)
> >> +{
> >> +       return 0;
> >> +}
> >> +NOKPROBE_SYMBOL(arch_trampoline_kprobe);
> >> +
> >>   int __init arch_init_kprobes(void)
> >>   {
> >>          return 0;
> >> diff --git a/arch/loongarch/kernel/kprobes_trampoline.S b/arch/loongarch/kernel/kprobes_trampoline.S
> >> new file mode 100644
> >> index 0000000..9888ab8
> >> --- /dev/null
> >> +++ b/arch/loongarch/kernel/kprobes_trampoline.S
> >> @@ -0,0 +1,97 @@
> >> +/* SPDX-License-Identifier: GPL-2.0+ */
> >> +#include <linux/linkage.h>
> >> +#include <asm/stackframe.h>
> >> +
> >> +       .text
> >> +
> >> +       .macro save_all_base_regs
> >> +       cfi_st  zero, PT_R0
> >> +       cfi_st  ra, PT_R1
> >> +       cfi_st  tp, PT_R2
> >> +       cfi_st  a0, PT_R4
> >> +       cfi_st  a1, PT_R5
> >> +       cfi_st  a2, PT_R6
> >> +       cfi_st  a3, PT_R7
> >> +       cfi_st  a4, PT_R8
> >> +       cfi_st  a5, PT_R9
> >> +       cfi_st  a6, PT_R10
> >> +       cfi_st  a7, PT_R11
> >> +       cfi_st  t0, PT_R12
> >> +       cfi_st  t1, PT_R13
> >> +       cfi_st  t2, PT_R14
> >> +       cfi_st  t3, PT_R15
> >> +       cfi_st  t4, PT_R16
> >> +       cfi_st  t5, PT_R17
> >> +       cfi_st  t6, PT_R18
> >> +       cfi_st  t7, PT_R19
> >> +       cfi_st  t8, PT_R20
> >> +       cfi_st  u0, PT_R21
> >> +       cfi_st  fp, PT_R22
> >> +       cfi_st  s0, PT_R23
> >> +       cfi_st  s1, PT_R24
> >> +       cfi_st  s2, PT_R25
> >> +       cfi_st  s3, PT_R26
> >> +       cfi_st  s4, PT_R27
> >> +       cfi_st  s5, PT_R28
> >> +       cfi_st  s6, PT_R29
> >> +       cfi_st  s7, PT_R30
> >> +       cfi_st  s8, PT_R31
> >> +       addi.d  t0, sp, PT_SIZE
> >> +       LONG_S  t0, sp, PT_R3
> >> +       csrrd   t0, LOONGARCH_CSR_CRMD
> >> +       andi    t0, t0, 0x7 /* extract bit[1:0] PLV, bit[2] IE */
> >> +       LONG_S  t0, sp, PT_PRMD
> >> +       .endm
> >> +
> >> +       .macro restore_all_base_regs
> >> +       cfi_ld  zero, PT_R0
> >> +       cfi_ld  tp, PT_R2
> >> +       cfi_ld  a0, PT_R4
> >> +       cfi_ld  a1, PT_R5
> >> +       cfi_ld  a2, PT_R6
> >> +       cfi_ld  a3, PT_R7
> >> +       cfi_ld  a4, PT_R8
> >> +       cfi_ld  a5, PT_R9
> >> +       cfi_ld  a6, PT_R10
> >> +       cfi_ld  a7, PT_R11
> >> +       cfi_ld  t0, PT_R12
> >> +       cfi_ld  t1, PT_R13
> >> +       cfi_ld  t2, PT_R14
> >> +       cfi_ld  t3, PT_R15
> >> +       cfi_ld  t4, PT_R16
> >> +       cfi_ld  t5, PT_R17
> >> +       cfi_ld  t6, PT_R18
> >> +       cfi_ld  t7, PT_R19
> >> +       cfi_ld  t8, PT_R20
> >> +       cfi_ld  u0, PT_R21
> >> +       cfi_ld  fp, PT_R22
> >> +       cfi_ld  s0, PT_R23
> >> +       cfi_ld  s1, PT_R24
> >> +       cfi_ld  s2, PT_R25
> >> +       cfi_ld  s3, PT_R26
> >> +       cfi_ld  s4, PT_R27
> >> +       cfi_ld  s5, PT_R28
> >> +       cfi_ld  s6, PT_R29
> >> +       cfi_ld  s7, PT_R30
> >> +       cfi_ld  s8, PT_R31
> >> +       LONG_L  t0, sp, PT_PRMD
> >> +       li.d    t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */
> >> +       csrxchg t0, t1, LOONGARCH_CSR_CRMD
> >> +       .endm
> > Do you think we need to save and restore all regs here?
> >
> > Huacai
>
> Hi, Huacai,
>
>
> Note that it is not function context. In the original kprobe design, it is
> triggered by 'break' and then trap into exception with all pt_regs saved.
> The all pt_regs will be visible to the user. So I think in this version
> we should also support all regs to user. BTW, due to all exceptions is
> trapped by 'break' something in pt_regs is not needed, like estat,
> badvaddr and so on.
OK, but I still have some questions:
1, Why $r0 need save/restore?
2, Why save $r1 but not restore?
3, What is the purpose of CRMD magic?

Huacai
>
>
> Thanks,
>
> Jinyang
>
> >> +
> >> +SYM_CODE_START(__kretprobe_trampoline)
> >> +       addi.d  sp, sp, -PT_SIZE
> >> +       save_all_base_regs
> >> +
> >> +       move a0, sp /* pt_regs */
> >> +
> >> +       bl trampoline_probe_handler
> >> +
> >> +       /* use the result as the return-address */
> >> +       move ra, a0
> >> +
> >> +       restore_all_base_regs
> >> +       addi.d  sp, sp, PT_SIZE
> >> +
> >> +       jr ra
> >> +SYM_CODE_END(__kretprobe_trampoline)
> >> --
> >> 2.1.0
> >>
> >>
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 3/5] LoongArch: Add kretprobe support
  2022-11-14  6:50       ` Huacai Chen
@ 2022-11-14  8:32         ` Jinyang He
  2022-11-14  8:51           ` Huacai Chen
  0 siblings, 1 reply; 16+ messages in thread
From: Jinyang He @ 2022-11-14  8:32 UTC (permalink / raw)
  To: Huacai Chen; +Cc: Tiezhu Yang, Masami Hiramatsu, loongarch, linux-kernel

On 2022/11/14 下午2:50, Huacai Chen wrote:

> On Mon, Nov 14, 2022 at 2:11 PM Jinyang He <hejinyang@loongson.cn> wrote:
>> On 2022/11/14 下午12:43, Huacai Chen wrote:
>>
>>> Hi, Tiezhu and Jinyang,
>>>
>>> On Wed, Sep 28, 2022 at 8:50 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>>>> Use the generic kretprobe trampoline handler to add kretprobe
>>>> support for LoongArch.
>>>>
>>>> Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
>>>> ---
>>>>    arch/loongarch/Kconfig                     |  1 +
>>>>    arch/loongarch/kernel/Makefile             |  2 +-
>>>>    arch/loongarch/kernel/kprobes.c            | 24 ++++++++
>>>>    arch/loongarch/kernel/kprobes_trampoline.S | 97 ++++++++++++++++++++++++++++++
>>>>    4 files changed, 123 insertions(+), 1 deletion(-)
>>>>    create mode 100644 arch/loongarch/kernel/kprobes_trampoline.S
>>>>
>>>> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
>>>> index 8debd70..877be6a 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/Makefile b/arch/loongarch/kernel/Makefile
>>>> index ff98d8a..48f50607 100644
>>>> --- a/arch/loongarch/kernel/Makefile
>>>> +++ b/arch/loongarch/kernel/Makefile
>>>> @@ -33,6 +33,6 @@ obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o
>>>>
>>>>    obj-$(CONFIG_PERF_EVENTS)      += perf_event.o perf_regs.o
>>>>
>>>> -obj-$(CONFIG_KPROBES)          += kprobes.o
>>>> +obj-$(CONFIG_KPROBES)          += kprobes.o kprobes_trampoline.o
>>>>
>>>>    CPPFLAGS_vmlinux.lds           := $(KBUILD_CFLAGS)
>>>> diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
>>>> index c11f6e0..ca3f1dc 100644
>>>> --- a/arch/loongarch/kernel/kprobes.c
>>>> +++ b/arch/loongarch/kernel/kprobes.c
>>>> @@ -306,6 +306,30 @@ int __init arch_populate_kprobe_blacklist(void)
>>>>                                            (unsigned long)__irqentry_text_end);
>>>>    }
>>>>
>>>> +/* Called from __kretprobe_trampoline */
>>>> +void __used *trampoline_probe_handler(struct pt_regs *regs)
>>>> +{
>>>> +       return (void *)kretprobe_trampoline_handler(regs, NULL);
>>>> +}
>>>> +NOKPROBE_SYMBOL(trampoline_probe_handler);
>>>> +
>>>> +void arch_prepare_kretprobe(struct kretprobe_instance *ri,
>>>> +                           struct pt_regs *regs)
>>>> +{
>>>> +       ri->ret_addr = (kprobe_opcode_t *)regs->regs[1];
>>>> +       ri->fp = NULL;
>>>> +
>>>> +       /* Replace the return addr with trampoline addr */
>>>> +       regs->regs[1] = (unsigned long)&__kretprobe_trampoline;
>>>> +}
>>>> +NOKPROBE_SYMBOL(arch_prepare_kretprobe);
>>>> +
>>>> +int arch_trampoline_kprobe(struct kprobe *p)
>>>> +{
>>>> +       return 0;
>>>> +}
>>>> +NOKPROBE_SYMBOL(arch_trampoline_kprobe);
>>>> +
>>>>    int __init arch_init_kprobes(void)
>>>>    {
>>>>           return 0;
>>>> diff --git a/arch/loongarch/kernel/kprobes_trampoline.S b/arch/loongarch/kernel/kprobes_trampoline.S
>>>> new file mode 100644
>>>> index 0000000..9888ab8
>>>> --- /dev/null
>>>> +++ b/arch/loongarch/kernel/kprobes_trampoline.S
>>>> @@ -0,0 +1,97 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>>> +#include <linux/linkage.h>
>>>> +#include <asm/stackframe.h>
>>>> +
>>>> +       .text
>>>> +
>>>> +       .macro save_all_base_regs
>>>> +       cfi_st  zero, PT_R0
>>>> +       cfi_st  ra, PT_R1
>>>> +       cfi_st  tp, PT_R2
>>>> +       cfi_st  a0, PT_R4
>>>> +       cfi_st  a1, PT_R5
>>>> +       cfi_st  a2, PT_R6
>>>> +       cfi_st  a3, PT_R7
>>>> +       cfi_st  a4, PT_R8
>>>> +       cfi_st  a5, PT_R9
>>>> +       cfi_st  a6, PT_R10
>>>> +       cfi_st  a7, PT_R11
>>>> +       cfi_st  t0, PT_R12
>>>> +       cfi_st  t1, PT_R13
>>>> +       cfi_st  t2, PT_R14
>>>> +       cfi_st  t3, PT_R15
>>>> +       cfi_st  t4, PT_R16
>>>> +       cfi_st  t5, PT_R17
>>>> +       cfi_st  t6, PT_R18
>>>> +       cfi_st  t7, PT_R19
>>>> +       cfi_st  t8, PT_R20
>>>> +       cfi_st  u0, PT_R21
>>>> +       cfi_st  fp, PT_R22
>>>> +       cfi_st  s0, PT_R23
>>>> +       cfi_st  s1, PT_R24
>>>> +       cfi_st  s2, PT_R25
>>>> +       cfi_st  s3, PT_R26
>>>> +       cfi_st  s4, PT_R27
>>>> +       cfi_st  s5, PT_R28
>>>> +       cfi_st  s6, PT_R29
>>>> +       cfi_st  s7, PT_R30
>>>> +       cfi_st  s8, PT_R31
>>>> +       addi.d  t0, sp, PT_SIZE
>>>> +       LONG_S  t0, sp, PT_R3
>>>> +       csrrd   t0, LOONGARCH_CSR_CRMD
>>>> +       andi    t0, t0, 0x7 /* extract bit[1:0] PLV, bit[2] IE */
>>>> +       LONG_S  t0, sp, PT_PRMD
>>>> +       .endm
>>>> +
>>>> +       .macro restore_all_base_regs
>>>> +       cfi_ld  zero, PT_R0
>>>> +       cfi_ld  tp, PT_R2
>>>> +       cfi_ld  a0, PT_R4
>>>> +       cfi_ld  a1, PT_R5
>>>> +       cfi_ld  a2, PT_R6
>>>> +       cfi_ld  a3, PT_R7
>>>> +       cfi_ld  a4, PT_R8
>>>> +       cfi_ld  a5, PT_R9
>>>> +       cfi_ld  a6, PT_R10
>>>> +       cfi_ld  a7, PT_R11
>>>> +       cfi_ld  t0, PT_R12
>>>> +       cfi_ld  t1, PT_R13
>>>> +       cfi_ld  t2, PT_R14
>>>> +       cfi_ld  t3, PT_R15
>>>> +       cfi_ld  t4, PT_R16
>>>> +       cfi_ld  t5, PT_R17
>>>> +       cfi_ld  t6, PT_R18
>>>> +       cfi_ld  t7, PT_R19
>>>> +       cfi_ld  t8, PT_R20
>>>> +       cfi_ld  u0, PT_R21
>>>> +       cfi_ld  fp, PT_R22
>>>> +       cfi_ld  s0, PT_R23
>>>> +       cfi_ld  s1, PT_R24
>>>> +       cfi_ld  s2, PT_R25
>>>> +       cfi_ld  s3, PT_R26
>>>> +       cfi_ld  s4, PT_R27
>>>> +       cfi_ld  s5, PT_R28
>>>> +       cfi_ld  s6, PT_R29
>>>> +       cfi_ld  s7, PT_R30
>>>> +       cfi_ld  s8, PT_R31
>>>> +       LONG_L  t0, sp, PT_PRMD
>>>> +       li.d    t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */
>>>> +       csrxchg t0, t1, LOONGARCH_CSR_CRMD
>>>> +       .endm
>>> Do you think we need to save and restore all regs here?
>>>
>>> Huacai
>> Hi, Huacai,
>>
>>
>> Note that it is not function context. In the original kprobe design, it is
>> triggered by 'break' and then trap into exception with all pt_regs saved.
>> The all pt_regs will be visible to the user. So I think in this version
>> we should also support all regs to user. BTW, due to all exceptions is
>> trapped by 'break' something in pt_regs is not needed, like estat,
>> badvaddr and so on.
> OK, but I still have some questions:
> 1, Why $r0 need save/restore?

Surely $r0 can be not saved, as now we do not have strange purpose
to make PT_R0 as a flag.


> 2, Why save $r1 but not restore?
My wrong idea is $r1 should be saved at CSR_ERA, to plays it like
exception happened. But its value always equal the address of
__kretprobe_trampoline. The kretprobe is something like fgraph. The real
return address is returned by trampoline_probe_handler. And at present,
the real return address is replaced in pt_regs->csr_era in
__kretprobe_trampoline_handler(). So the $r1 saved in CSR_ERA will
be destroied at __kretprobe_trampoline_handler() actually.
That's why $r1 saved also is not needed.

And both way to get return address from return value or get return address

from pt_regs is same on LoongArch because arch_kretprobe_fixup_return()

does nothing. But I think get return address from pt_regs is more reliable.


> 3, What is the purpose of CRMD magic?

PT_CRMD magic is just exception context. It gives us a chance e.g.
set ie off at func head, and ie on at func return.


Thanks,

Jinyang

>
>>>> +
>>>> +SYM_CODE_START(__kretprobe_trampoline)
>>>> +       addi.d  sp, sp, -PT_SIZE
>>>> +       save_all_base_regs
>>>> +
>>>> +       move a0, sp /* pt_regs */
>>>> +
>>>> +       bl trampoline_probe_handler
>>>> +
>>>> +       /* use the result as the return-address */
>>>> +       move ra, a0
>>>> +
>>>> +       restore_all_base_regs
>>>> +       addi.d  sp, sp, PT_SIZE
>>>> +
>>>> +       jr ra
>>>> +SYM_CODE_END(__kretprobe_trampoline)
>>>> --
>>>> 2.1.0
>>>>
>>>>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 3/5] LoongArch: Add kretprobe support
  2022-11-14  8:32         ` Jinyang He
@ 2022-11-14  8:51           ` Huacai Chen
  2022-11-14  9:26             ` Jinyang He
  0 siblings, 1 reply; 16+ messages in thread
From: Huacai Chen @ 2022-11-14  8:51 UTC (permalink / raw)
  To: Jinyang He; +Cc: Tiezhu Yang, Masami Hiramatsu, loongarch, linux-kernel

On Mon, Nov 14, 2022 at 4:32 PM Jinyang He <hejinyang@loongson.cn> wrote:
>
> On 2022/11/14 下午2:50, Huacai Chen wrote:
>
> > On Mon, Nov 14, 2022 at 2:11 PM Jinyang He <hejinyang@loongson.cn> wrote:
> >> On 2022/11/14 下午12:43, Huacai Chen wrote:
> >>
> >>> Hi, Tiezhu and Jinyang,
> >>>
> >>> On Wed, Sep 28, 2022 at 8:50 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
> >>>> Use the generic kretprobe trampoline handler to add kretprobe
> >>>> support for LoongArch.
> >>>>
> >>>> Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
> >>>> ---
> >>>>    arch/loongarch/Kconfig                     |  1 +
> >>>>    arch/loongarch/kernel/Makefile             |  2 +-
> >>>>    arch/loongarch/kernel/kprobes.c            | 24 ++++++++
> >>>>    arch/loongarch/kernel/kprobes_trampoline.S | 97 ++++++++++++++++++++++++++++++
> >>>>    4 files changed, 123 insertions(+), 1 deletion(-)
> >>>>    create mode 100644 arch/loongarch/kernel/kprobes_trampoline.S
> >>>>
> >>>> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> >>>> index 8debd70..877be6a 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/Makefile b/arch/loongarch/kernel/Makefile
> >>>> index ff98d8a..48f50607 100644
> >>>> --- a/arch/loongarch/kernel/Makefile
> >>>> +++ b/arch/loongarch/kernel/Makefile
> >>>> @@ -33,6 +33,6 @@ obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o
> >>>>
> >>>>    obj-$(CONFIG_PERF_EVENTS)      += perf_event.o perf_regs.o
> >>>>
> >>>> -obj-$(CONFIG_KPROBES)          += kprobes.o
> >>>> +obj-$(CONFIG_KPROBES)          += kprobes.o kprobes_trampoline.o
> >>>>
> >>>>    CPPFLAGS_vmlinux.lds           := $(KBUILD_CFLAGS)
> >>>> diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
> >>>> index c11f6e0..ca3f1dc 100644
> >>>> --- a/arch/loongarch/kernel/kprobes.c
> >>>> +++ b/arch/loongarch/kernel/kprobes.c
> >>>> @@ -306,6 +306,30 @@ int __init arch_populate_kprobe_blacklist(void)
> >>>>                                            (unsigned long)__irqentry_text_end);
> >>>>    }
> >>>>
> >>>> +/* Called from __kretprobe_trampoline */
> >>>> +void __used *trampoline_probe_handler(struct pt_regs *regs)
> >>>> +{
> >>>> +       return (void *)kretprobe_trampoline_handler(regs, NULL);
> >>>> +}
> >>>> +NOKPROBE_SYMBOL(trampoline_probe_handler);
> >>>> +
> >>>> +void arch_prepare_kretprobe(struct kretprobe_instance *ri,
> >>>> +                           struct pt_regs *regs)
> >>>> +{
> >>>> +       ri->ret_addr = (kprobe_opcode_t *)regs->regs[1];
> >>>> +       ri->fp = NULL;
> >>>> +
> >>>> +       /* Replace the return addr with trampoline addr */
> >>>> +       regs->regs[1] = (unsigned long)&__kretprobe_trampoline;
> >>>> +}
> >>>> +NOKPROBE_SYMBOL(arch_prepare_kretprobe);
> >>>> +
> >>>> +int arch_trampoline_kprobe(struct kprobe *p)
> >>>> +{
> >>>> +       return 0;
> >>>> +}
> >>>> +NOKPROBE_SYMBOL(arch_trampoline_kprobe);
> >>>> +
> >>>>    int __init arch_init_kprobes(void)
> >>>>    {
> >>>>           return 0;
> >>>> diff --git a/arch/loongarch/kernel/kprobes_trampoline.S b/arch/loongarch/kernel/kprobes_trampoline.S
> >>>> new file mode 100644
> >>>> index 0000000..9888ab8
> >>>> --- /dev/null
> >>>> +++ b/arch/loongarch/kernel/kprobes_trampoline.S
> >>>> @@ -0,0 +1,97 @@
> >>>> +/* SPDX-License-Identifier: GPL-2.0+ */
> >>>> +#include <linux/linkage.h>
> >>>> +#include <asm/stackframe.h>
> >>>> +
> >>>> +       .text
> >>>> +
> >>>> +       .macro save_all_base_regs
> >>>> +       cfi_st  zero, PT_R0
> >>>> +       cfi_st  ra, PT_R1
> >>>> +       cfi_st  tp, PT_R2
> >>>> +       cfi_st  a0, PT_R4
> >>>> +       cfi_st  a1, PT_R5
> >>>> +       cfi_st  a2, PT_R6
> >>>> +       cfi_st  a3, PT_R7
> >>>> +       cfi_st  a4, PT_R8
> >>>> +       cfi_st  a5, PT_R9
> >>>> +       cfi_st  a6, PT_R10
> >>>> +       cfi_st  a7, PT_R11
> >>>> +       cfi_st  t0, PT_R12
> >>>> +       cfi_st  t1, PT_R13
> >>>> +       cfi_st  t2, PT_R14
> >>>> +       cfi_st  t3, PT_R15
> >>>> +       cfi_st  t4, PT_R16
> >>>> +       cfi_st  t5, PT_R17
> >>>> +       cfi_st  t6, PT_R18
> >>>> +       cfi_st  t7, PT_R19
> >>>> +       cfi_st  t8, PT_R20
> >>>> +       cfi_st  u0, PT_R21
> >>>> +       cfi_st  fp, PT_R22
> >>>> +       cfi_st  s0, PT_R23
> >>>> +       cfi_st  s1, PT_R24
> >>>> +       cfi_st  s2, PT_R25
> >>>> +       cfi_st  s3, PT_R26
> >>>> +       cfi_st  s4, PT_R27
> >>>> +       cfi_st  s5, PT_R28
> >>>> +       cfi_st  s6, PT_R29
> >>>> +       cfi_st  s7, PT_R30
> >>>> +       cfi_st  s8, PT_R31
> >>>> +       addi.d  t0, sp, PT_SIZE
> >>>> +       LONG_S  t0, sp, PT_R3
> >>>> +       csrrd   t0, LOONGARCH_CSR_CRMD
> >>>> +       andi    t0, t0, 0x7 /* extract bit[1:0] PLV, bit[2] IE */
> >>>> +       LONG_S  t0, sp, PT_PRMD
> >>>> +       .endm
> >>>> +
> >>>> +       .macro restore_all_base_regs
> >>>> +       cfi_ld  zero, PT_R0
> >>>> +       cfi_ld  tp, PT_R2
> >>>> +       cfi_ld  a0, PT_R4
> >>>> +       cfi_ld  a1, PT_R5
> >>>> +       cfi_ld  a2, PT_R6
> >>>> +       cfi_ld  a3, PT_R7
> >>>> +       cfi_ld  a4, PT_R8
> >>>> +       cfi_ld  a5, PT_R9
> >>>> +       cfi_ld  a6, PT_R10
> >>>> +       cfi_ld  a7, PT_R11
> >>>> +       cfi_ld  t0, PT_R12
> >>>> +       cfi_ld  t1, PT_R13
> >>>> +       cfi_ld  t2, PT_R14
> >>>> +       cfi_ld  t3, PT_R15
> >>>> +       cfi_ld  t4, PT_R16
> >>>> +       cfi_ld  t5, PT_R17
> >>>> +       cfi_ld  t6, PT_R18
> >>>> +       cfi_ld  t7, PT_R19
> >>>> +       cfi_ld  t8, PT_R20
> >>>> +       cfi_ld  u0, PT_R21
> >>>> +       cfi_ld  fp, PT_R22
> >>>> +       cfi_ld  s0, PT_R23
> >>>> +       cfi_ld  s1, PT_R24
> >>>> +       cfi_ld  s2, PT_R25
> >>>> +       cfi_ld  s3, PT_R26
> >>>> +       cfi_ld  s4, PT_R27
> >>>> +       cfi_ld  s5, PT_R28
> >>>> +       cfi_ld  s6, PT_R29
> >>>> +       cfi_ld  s7, PT_R30
> >>>> +       cfi_ld  s8, PT_R31
> >>>> +       LONG_L  t0, sp, PT_PRMD
> >>>> +       li.d    t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */
> >>>> +       csrxchg t0, t1, LOONGARCH_CSR_CRMD
> >>>> +       .endm
> >>> Do you think we need to save and restore all regs here?
> >>>
> >>> Huacai
> >> Hi, Huacai,
> >>
> >>
> >> Note that it is not function context. In the original kprobe design, it is
> >> triggered by 'break' and then trap into exception with all pt_regs saved.
> >> The all pt_regs will be visible to the user. So I think in this version
> >> we should also support all regs to user. BTW, due to all exceptions is
> >> trapped by 'break' something in pt_regs is not needed, like estat,
> >> badvaddr and so on.
> > OK, but I still have some questions:
> > 1, Why $r0 need save/restore?
>
> Surely $r0 can be not saved, as now we do not have strange purpose
> to make PT_R0 as a flag.
>
>
> > 2, Why save $r1 but not restore?
> My wrong idea is $r1 should be saved at CSR_ERA, to plays it like
> exception happened. But its value always equal the address of
> __kretprobe_trampoline. The kretprobe is something like fgraph. The real
> return address is returned by trampoline_probe_handler. And at present,
> the real return address is replaced in pt_regs->csr_era in
> __kretprobe_trampoline_handler(). So the $r1 saved in CSR_ERA will
> be destroied at __kretprobe_trampoline_handler() actually.
> That's why $r1 saved also is not needed.
>
> And both way to get return address from return value or get return address
>
> from pt_regs is same on LoongArch because arch_kretprobe_fixup_return()
>
> does nothing. But I think get return address from pt_regs is more reliable.
>
>
> > 3, What is the purpose of CRMD magic?
>
> PT_CRMD magic is just exception context. It gives us a chance e.g.
> set ie off at func head, and ie on at func return.
ARM64 and RISC-V don't have such magics, so maybe they are unneeded?

Huacai
>
>
> Thanks,
>
> Jinyang
>
> >
> >>>> +
> >>>> +SYM_CODE_START(__kretprobe_trampoline)
> >>>> +       addi.d  sp, sp, -PT_SIZE
> >>>> +       save_all_base_regs
> >>>> +
> >>>> +       move a0, sp /* pt_regs */
> >>>> +
> >>>> +       bl trampoline_probe_handler
> >>>> +
> >>>> +       /* use the result as the return-address */
> >>>> +       move ra, a0
> >>>> +
> >>>> +       restore_all_base_regs
> >>>> +       addi.d  sp, sp, PT_SIZE
> >>>> +
> >>>> +       jr ra
> >>>> +SYM_CODE_END(__kretprobe_trampoline)
> >>>> --
> >>>> 2.1.0
> >>>>
> >>>>
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 3/5] LoongArch: Add kretprobe support
  2022-11-14  8:51           ` Huacai Chen
@ 2022-11-14  9:26             ` Jinyang He
  0 siblings, 0 replies; 16+ messages in thread
From: Jinyang He @ 2022-11-14  9:26 UTC (permalink / raw)
  To: Huacai Chen; +Cc: Tiezhu Yang, Masami Hiramatsu, loongarch, linux-kernel

On 2022/11/14 下午4:51, Huacai Chen wrote:

> On Mon, Nov 14, 2022 at 4:32 PM Jinyang He <hejinyang@loongson.cn> wrote:
>> On 2022/11/14 下午2:50, Huacai Chen wrote:
>>
>>> On Mon, Nov 14, 2022 at 2:11 PM Jinyang He <hejinyang@loongson.cn> wrote:
>>>> On 2022/11/14 下午12:43, Huacai Chen wrote:
>>>>
>>>>> Hi, Tiezhu and Jinyang,
>>>>>
>>>>> On Wed, Sep 28, 2022 at 8:50 AM Tiezhu Yang <yangtiezhu@loongson.cn> wrote:
>>>>>> Use the generic kretprobe trampoline handler to add kretprobe
>>>>>> support for LoongArch.
>>>>>>
>>>>>> Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
>>>>>> ---
>>>>>>     arch/loongarch/Kconfig                     |  1 +
>>>>>>     arch/loongarch/kernel/Makefile             |  2 +-
>>>>>>     arch/loongarch/kernel/kprobes.c            | 24 ++++++++
>>>>>>     arch/loongarch/kernel/kprobes_trampoline.S | 97 ++++++++++++++++++++++++++++++
>>>>>>     4 files changed, 123 insertions(+), 1 deletion(-)
>>>>>>     create mode 100644 arch/loongarch/kernel/kprobes_trampoline.S
>>>>>>
>>>>>> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
>>>>>> index 8debd70..877be6a 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/Makefile b/arch/loongarch/kernel/Makefile
>>>>>> index ff98d8a..48f50607 100644
>>>>>> --- a/arch/loongarch/kernel/Makefile
>>>>>> +++ b/arch/loongarch/kernel/Makefile
>>>>>> @@ -33,6 +33,6 @@ obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o
>>>>>>
>>>>>>     obj-$(CONFIG_PERF_EVENTS)      += perf_event.o perf_regs.o
>>>>>>
>>>>>> -obj-$(CONFIG_KPROBES)          += kprobes.o
>>>>>> +obj-$(CONFIG_KPROBES)          += kprobes.o kprobes_trampoline.o
>>>>>>
>>>>>>     CPPFLAGS_vmlinux.lds           := $(KBUILD_CFLAGS)
>>>>>> diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
>>>>>> index c11f6e0..ca3f1dc 100644
>>>>>> --- a/arch/loongarch/kernel/kprobes.c
>>>>>> +++ b/arch/loongarch/kernel/kprobes.c
>>>>>> @@ -306,6 +306,30 @@ int __init arch_populate_kprobe_blacklist(void)
>>>>>>                                             (unsigned long)__irqentry_text_end);
>>>>>>     }
>>>>>>
>>>>>> +/* Called from __kretprobe_trampoline */
>>>>>> +void __used *trampoline_probe_handler(struct pt_regs *regs)
>>>>>> +{
>>>>>> +       return (void *)kretprobe_trampoline_handler(regs, NULL);
>>>>>> +}
>>>>>> +NOKPROBE_SYMBOL(trampoline_probe_handler);
>>>>>> +
>>>>>> +void arch_prepare_kretprobe(struct kretprobe_instance *ri,
>>>>>> +                           struct pt_regs *regs)
>>>>>> +{
>>>>>> +       ri->ret_addr = (kprobe_opcode_t *)regs->regs[1];
>>>>>> +       ri->fp = NULL;
>>>>>> +
>>>>>> +       /* Replace the return addr with trampoline addr */
>>>>>> +       regs->regs[1] = (unsigned long)&__kretprobe_trampoline;
>>>>>> +}
>>>>>> +NOKPROBE_SYMBOL(arch_prepare_kretprobe);
>>>>>> +
>>>>>> +int arch_trampoline_kprobe(struct kprobe *p)
>>>>>> +{
>>>>>> +       return 0;
>>>>>> +}
>>>>>> +NOKPROBE_SYMBOL(arch_trampoline_kprobe);
>>>>>> +
>>>>>>     int __init arch_init_kprobes(void)
>>>>>>     {
>>>>>>            return 0;
>>>>>> diff --git a/arch/loongarch/kernel/kprobes_trampoline.S b/arch/loongarch/kernel/kprobes_trampoline.S
>>>>>> new file mode 100644
>>>>>> index 0000000..9888ab8
>>>>>> --- /dev/null
>>>>>> +++ b/arch/loongarch/kernel/kprobes_trampoline.S
>>>>>> @@ -0,0 +1,97 @@
>>>>>> +/* SPDX-License-Identifier: GPL-2.0+ */
>>>>>> +#include <linux/linkage.h>
>>>>>> +#include <asm/stackframe.h>
>>>>>> +
>>>>>> +       .text
>>>>>> +
>>>>>> +       .macro save_all_base_regs
>>>>>> +       cfi_st  zero, PT_R0
>>>>>> +       cfi_st  ra, PT_R1
>>>>>> +       cfi_st  tp, PT_R2
>>>>>> +       cfi_st  a0, PT_R4
>>>>>> +       cfi_st  a1, PT_R5
>>>>>> +       cfi_st  a2, PT_R6
>>>>>> +       cfi_st  a3, PT_R7
>>>>>> +       cfi_st  a4, PT_R8
>>>>>> +       cfi_st  a5, PT_R9
>>>>>> +       cfi_st  a6, PT_R10
>>>>>> +       cfi_st  a7, PT_R11
>>>>>> +       cfi_st  t0, PT_R12
>>>>>> +       cfi_st  t1, PT_R13
>>>>>> +       cfi_st  t2, PT_R14
>>>>>> +       cfi_st  t3, PT_R15
>>>>>> +       cfi_st  t4, PT_R16
>>>>>> +       cfi_st  t5, PT_R17
>>>>>> +       cfi_st  t6, PT_R18
>>>>>> +       cfi_st  t7, PT_R19
>>>>>> +       cfi_st  t8, PT_R20
>>>>>> +       cfi_st  u0, PT_R21
>>>>>> +       cfi_st  fp, PT_R22
>>>>>> +       cfi_st  s0, PT_R23
>>>>>> +       cfi_st  s1, PT_R24
>>>>>> +       cfi_st  s2, PT_R25
>>>>>> +       cfi_st  s3, PT_R26
>>>>>> +       cfi_st  s4, PT_R27
>>>>>> +       cfi_st  s5, PT_R28
>>>>>> +       cfi_st  s6, PT_R29
>>>>>> +       cfi_st  s7, PT_R30
>>>>>> +       cfi_st  s8, PT_R31
>>>>>> +       addi.d  t0, sp, PT_SIZE
>>>>>> +       LONG_S  t0, sp, PT_R3
>>>>>> +       csrrd   t0, LOONGARCH_CSR_CRMD
>>>>>> +       andi    t0, t0, 0x7 /* extract bit[1:0] PLV, bit[2] IE */
>>>>>> +       LONG_S  t0, sp, PT_PRMD
>>>>>> +       .endm
>>>>>> +
>>>>>> +       .macro restore_all_base_regs
>>>>>> +       cfi_ld  zero, PT_R0
>>>>>> +       cfi_ld  tp, PT_R2
>>>>>> +       cfi_ld  a0, PT_R4
>>>>>> +       cfi_ld  a1, PT_R5
>>>>>> +       cfi_ld  a2, PT_R6
>>>>>> +       cfi_ld  a3, PT_R7
>>>>>> +       cfi_ld  a4, PT_R8
>>>>>> +       cfi_ld  a5, PT_R9
>>>>>> +       cfi_ld  a6, PT_R10
>>>>>> +       cfi_ld  a7, PT_R11
>>>>>> +       cfi_ld  t0, PT_R12
>>>>>> +       cfi_ld  t1, PT_R13
>>>>>> +       cfi_ld  t2, PT_R14
>>>>>> +       cfi_ld  t3, PT_R15
>>>>>> +       cfi_ld  t4, PT_R16
>>>>>> +       cfi_ld  t5, PT_R17
>>>>>> +       cfi_ld  t6, PT_R18
>>>>>> +       cfi_ld  t7, PT_R19
>>>>>> +       cfi_ld  t8, PT_R20
>>>>>> +       cfi_ld  u0, PT_R21
>>>>>> +       cfi_ld  fp, PT_R22
>>>>>> +       cfi_ld  s0, PT_R23
>>>>>> +       cfi_ld  s1, PT_R24
>>>>>> +       cfi_ld  s2, PT_R25
>>>>>> +       cfi_ld  s3, PT_R26
>>>>>> +       cfi_ld  s4, PT_R27
>>>>>> +       cfi_ld  s5, PT_R28
>>>>>> +       cfi_ld  s6, PT_R29
>>>>>> +       cfi_ld  s7, PT_R30
>>>>>> +       cfi_ld  s8, PT_R31
>>>>>> +       LONG_L  t0, sp, PT_PRMD
>>>>>> +       li.d    t1, 0x7 /* mask bit[1:0] PLV, bit[2] IE */
>>>>>> +       csrxchg t0, t1, LOONGARCH_CSR_CRMD
>>>>>> +       .endm
>>>>> Do you think we need to save and restore all regs here?
>>>>>
>>>>> Huacai
>>>> Hi, Huacai,
>>>>
>>>>
>>>> Note that it is not function context. In the original kprobe design, it is
>>>> triggered by 'break' and then trap into exception with all pt_regs saved.
>>>> The all pt_regs will be visible to the user. So I think in this version
>>>> we should also support all regs to user. BTW, due to all exceptions is
>>>> trapped by 'break' something in pt_regs is not needed, like estat,
>>>> badvaddr and so on.
>>> OK, but I still have some questions:
>>> 1, Why $r0 need save/restore?
>> Surely $r0 can be not saved, as now we do not have strange purpose
>> to make PT_R0 as a flag.
>>
>>
>>> 2, Why save $r1 but not restore?
>> My wrong idea is $r1 should be saved at CSR_ERA, to plays it like
>> exception happened. But its value always equal the address of
>> __kretprobe_trampoline. The kretprobe is something like fgraph. The real
>> return address is returned by trampoline_probe_handler. And at present,
>> the real return address is replaced in pt_regs->csr_era in
>> __kretprobe_trampoline_handler(). So the $r1 saved in CSR_ERA will
>> be destroied at __kretprobe_trampoline_handler() actually.
>> That's why $r1 saved also is not needed.
>>
>> And both way to get return address from return value or get return address
>>
>> from pt_regs is same on LoongArch because arch_kretprobe_fixup_return()
>>
>> does nothing. But I think get return address from pt_regs is more reliable.
>>
>>
>>> 3, What is the purpose of CRMD magic?
>> PT_CRMD magic is just exception context. It gives us a chance e.g.
>> set ie off at func head, and ie on at func return.
> ARM64 and RISC-V don't have such magics, so maybe they are unneeded?
>
ARM64 have noted "Construct a useful saved PSTATE" in kprobes_trampoline.S.

And it seems we provide more interfaces than RISC-V. ?


Thanks,

Jinyang


>>>>>> +
>>>>>> +SYM_CODE_START(__kretprobe_trampoline)
>>>>>> +       addi.d  sp, sp, -PT_SIZE
>>>>>> +       save_all_base_regs
>>>>>> +
>>>>>> +       move a0, sp /* pt_regs */
>>>>>> +
>>>>>> +       bl trampoline_probe_handler
>>>>>> +
>>>>>> +       /* use the result as the return-address */
>>>>>> +       move ra, a0
>>>>>> +
>>>>>> +       restore_all_base_regs
>>>>>> +       addi.d  sp, sp, PT_SIZE
>>>>>> +
>>>>>> +       jr ra
>>>>>> +SYM_CODE_END(__kretprobe_trampoline)
>>>>>> --
>>>>>> 2.1.0
>>>>>>
>>>>>>


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch
  2022-11-14  4:31 ` [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Huacai Chen
@ 2022-11-15  8:44   ` Tiezhu Yang
  0 siblings, 0 replies; 16+ messages in thread
From: Tiezhu Yang @ 2022-11-15  8:44 UTC (permalink / raw)
  To: Huacai Chen; +Cc: Masami Hiramatsu, loongarch, linux-kernel



On 11/14/2022 12:31 PM, Huacai Chen wrote:
> Hi, Tiezhu,
>
> Thanks for your patches, however,
> 1, You should rebase your code, since some functions such as
> is_pc_insn is provided in the github's loongarch-next branch;
> 2, Your code may have some problems about preemption, you can do
> something like this commit [1];
> 3, I don't think Kprobe should be enabled by default, distribution
> configs can enable it if needed.
>
> [1] https://github.com/loongson/linux/commit/023d5412ab5c154b87d57d70d465a4243df53717
>

OK, thank you, I will rebase and address the review comments,
and then send v3 later.

Thanks,
Tiezhu


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2022-11-15  8:44 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-28  0:50 [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Tiezhu Yang
2022-09-28  0:50 ` [PATCH v2 1/5] LoongArch: Simulate branch and PC instructions Tiezhu Yang
2022-11-14  4:37   ` Huacai Chen
2022-09-28  0:50 ` [PATCH v2 2/5] LoongArch: Add kprobe support Tiezhu Yang
2022-11-14  4:41   ` Huacai Chen
2022-09-28  0:50 ` [PATCH v2 3/5] LoongArch: Add kretprobe support Tiezhu Yang
2022-11-14  4:43   ` Huacai Chen
2022-11-14  6:11     ` Jinyang He
2022-11-14  6:50       ` Huacai Chen
2022-11-14  8:32         ` Jinyang He
2022-11-14  8:51           ` Huacai Chen
2022-11-14  9:26             ` Jinyang He
2022-09-28  0:50 ` [PATCH v2 4/5] samples/kprobes: Add LoongArch support Tiezhu Yang
2022-09-28  0:50 ` [PATCH v2 5/5] LoongArch: Enable KPROBES in default config Tiezhu Yang
2022-11-14  4:31 ` [PATCH v2 0/5] Add kprobe and kretprobe support for LoongArch Huacai Chen
2022-11-15  8:44   ` Tiezhu Yang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).