* [RFC 0/2] openrisc: Add support for KProbes
@ 2026-04-07 18:56 Sahil Siddiq
2026-04-07 18:56 ` [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
` (2 more replies)
0 siblings, 3 replies; 15+ messages in thread
From: Sahil Siddiq @ 2026-04-07 18:56 UTC (permalink / raw)
To: jonas, stefan.kristiansson, shorne, naveen, davem, mhiramat
Cc: peterz, jpoimboe, jbaron, rostedt, ardb, chenmiao.ku, johannes,
nsc, masahiroy, tytso, linux-openrisc, linux-kernel,
linux-trace-kernel, Sahil Siddiq
Hi,
This series adds basic support for KProbes on OpenRISC. There are
a few changes that I would still like to add and test before this
can be considered for merging. I was hoping to get some feedback on
the changes made so far. The implementation in this series is based
on KProbes for LoongArch, MIPS and RISC-V.
The current state of the series allows traps to be inserted dynamically
in the kernel. A KProbe can be inserted via a kernel module whose
init/exit functions are used to register/unregister a KProbe. A pre-
handler and post-handler can also be provisioned in the module, which
are associated with the KProbe and triggered when the probe is hit. See
the documentation on KProbes for a detailed explanation [1].
The following are yet to be implemented for OpenRISC:
1. kretprobes
2. kprobe-based event tracing
3. ftrace, and kprobe features that depend on ftrace (particularly,
dynamic tracing)
I hope to submit a patch for kretprobes soon (possibly in a revision of
this series).
I wrote a couple of kernel modules to test these changes. They can be found
here [2]. I also ran test_kprobes located at ./lib/tests/ against these
changes [3]. The results are as shown below:
/home # insmod test_kprobes.ko
KTAP version 1
1..1
KTAP version 1
# Subtest: kprobes_test
# module: test_kprobes
1..3
ok 1 test_kprobe
ok 2 test_kprobes
ok 3 test_kprobe_missed
# kprobes_test: pass:3 fail:0 skip:0 total:3
# Totals: pass:3 fail:0 skip:0 total:3
ok 1 kprobes_test
/home #
When compiling the kernel, the following options should be enabled:
1. CONFIG_HAVE_KPROBES=y
2. CONFIG_KPROBES=y
Also ensure that CONFIG_KPROBE_EVENTS is disabled.
To compile /lib/tests/test_kprobes.c, add the following to .config:
1. CONFIG_KUNIT=y
2. CONFIG_DEBUG_KERNEL=y
3. CONFIG_KPROBES_SANITY_TEST=m
The first commit cleans up and reorganizes existing functions, fixes
a few issues with instruction simulation, and introduces new structures
and macros that will be used by KProbes and other tracing facilities
in the future.
The second commit adds support for KProbes. Currently, I have
implemented this in such a way that KProbes can't be used to probe
a few "blacklisted" instructions. Probes can't be inserted in a delay
slot either (similar to MIPS). I have also added a few asm functions
to the blacklist that I think should not be probed. For e.g., "memset"
and "_trap_handler" have been blacklisted because probing them causes
the kernel to hang. However, I am not sure if other functions in "entry.S"
need to be added as well to the blacklist.
Thanks,
Sahil
[1] https://www.kernel.org/doc/html/latest/trace/kprobes.html
[2] https://github.com/valdaarhun/or-dev/tree/main/home
[3] https://github.com/openrisc/linux/blob/for-next/lib/tests/test_kprobes.c
Sahil Siddiq (2):
openrisc: Add utilities and clean up simulation of instructions
openrisc: Add KProbes
arch/openrisc/Kconfig | 1 +
arch/openrisc/configs/or1ksim_defconfig | 2 +
arch/openrisc/configs/virt_defconfig | 2 +
arch/openrisc/include/asm/asm.h | 22 ++
arch/openrisc/include/asm/break.h | 19 ++
arch/openrisc/include/asm/insn-def.h | 61 +++-
arch/openrisc/include/asm/kprobes.h | 76 +++++
arch/openrisc/include/asm/spr_defs.h | 1 +
arch/openrisc/kernel/Makefile | 3 +-
arch/openrisc/kernel/entry.S | 16 +
arch/openrisc/kernel/insn.c | 74 +++++
arch/openrisc/kernel/jump_label.c | 2 +-
arch/openrisc/kernel/kprobes.c | 381 ++++++++++++++++++++++++
arch/openrisc/kernel/traps.c | 67 ++---
arch/openrisc/lib/memcpy.c | 2 +
arch/openrisc/lib/memset.S | 4 +
arch/openrisc/mm/fault.c | 5 +
samples/kprobes/kprobe_example.c | 8 +
18 files changed, 701 insertions(+), 45 deletions(-)
create mode 100644 arch/openrisc/include/asm/asm.h
create mode 100644 arch/openrisc/include/asm/break.h
create mode 100644 arch/openrisc/include/asm/kprobes.h
create mode 100644 arch/openrisc/kernel/insn.c
create mode 100644 arch/openrisc/kernel/kprobes.c
--
2.53.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions
2026-04-07 18:56 [RFC 0/2] openrisc: Add support for KProbes Sahil Siddiq
@ 2026-04-07 18:56 ` Sahil Siddiq
2026-04-14 17:11 ` Stafford Horne
` (5 more replies)
2026-04-07 18:56 ` [RFC 2/2] openrisc: Add KProbes Sahil Siddiq
2026-04-15 6:48 ` [RFC 0/2] openrisc: Add support for KProbes Masami Hiramatsu
2 siblings, 6 replies; 15+ messages in thread
From: Sahil Siddiq @ 2026-04-07 18:56 UTC (permalink / raw)
To: jonas, stefan.kristiansson, shorne, naveen, davem, mhiramat
Cc: peterz, jpoimboe, jbaron, rostedt, ardb, chenmiao.ku, johannes,
nsc, masahiroy, tytso, linux-openrisc, linux-kernel,
linux-trace-kernel, Sahil Siddiq
Introduce new instruction-related utilities and macros for OpenRISC.
This is in preparation for patches that add tracing support such as
KProbes.
Simulate l.adrp. Fix bugs in simulation of l.jal and l.jalr. Earlier,
PC was being updated and then saved in the link register r9, resulting
in a corrupted page table (bad page map in process). Instead, update
PC after storing it in r9.
Move instruction simulation to its own file to enable reuse. Clean it
up and replace hardcoded values with computed expressions.
Link: https://raw.githubusercontent.com/openrisc/doc/master/openrisc-arch-1.4-rev0.pdf
Signed-off-by: Sahil Siddiq <sahilcdq0@gmail.com>
---
arch/openrisc/include/asm/insn-def.h | 61 +++++++++++++++++++++--
arch/openrisc/include/asm/spr_defs.h | 1 +
arch/openrisc/kernel/Makefile | 2 +-
arch/openrisc/kernel/insn.c | 74 ++++++++++++++++++++++++++++
arch/openrisc/kernel/jump_label.c | 2 +-
arch/openrisc/kernel/traps.c | 41 +--------------
6 files changed, 136 insertions(+), 45 deletions(-)
create mode 100644 arch/openrisc/kernel/insn.c
diff --git a/arch/openrisc/include/asm/insn-def.h b/arch/openrisc/include/asm/insn-def.h
index 1e0c028a5b95..c98f9770c52e 100644
--- a/arch/openrisc/include/asm/insn-def.h
+++ b/arch/openrisc/include/asm/insn-def.h
@@ -3,13 +3,66 @@
* Copyright (C) 2025 Chen Miao
*/
+#include <asm/spr.h>
+#include <asm/spr_defs.h>
+
#ifndef __ASM_OPENRISC_INSN_DEF_H
#define __ASM_OPENRISC_INSN_DEF_H
-/* or1k instructions are always 32 bits. */
-#define OPENRISC_INSN_SIZE 4
-
/* or1k nop instruction code */
-#define OPENRISC_INSN_NOP 0x15000000U
+#define INSN_NOP 0x15000000U
+
+#define INSN_CSYNC 0x23000000U
+#define INSN_MSYNC 0x22000000U
+#define INSN_PSYNC 0x22800000U
+
+#define OPCODE_TRAP 0x21000000U
+#define OPCODE_SYS 0x20000000U
+#define OPCODE_MACRC 0x18010000U
+
+struct pt_regs;
+
+enum six_bit_opcodes {
+ l_rfe = 0x09,
+ l_lwa = 0x1b,
+ l_mfspr = 0x2d,
+ l_mtspr = 0x30,
+ l_swa = 0x33,
+ l_j = 0x00,
+ l_jal = 0x01,
+ l_adrp = 0x02,
+ l_bnf = 0x03,
+ l_bf = 0x04,
+ l_jr = 0x11,
+ l_jalr = 0x12,
+};
+
+struct insn {
+ unsigned int opcode: 6;
+ unsigned int operands: 26;
+};
+
+union openrisc_instruction {
+ unsigned int word;
+ struct insn opcodes_6bit;
+};
+
+#define OPENRISC_INSN_SIZE (sizeof(union openrisc_instruction))
+
+/* Helpers for working with l.trap */
+static inline unsigned long __emit_trap(unsigned int code)
+{
+ return (code & 0xffff) | OPCODE_TRAP;
+}
+
+static inline bool has_delay_slot(void)
+{
+ unsigned int cpucfgr = mfspr(SPR_CPUCFGR);
+
+ return !(cpucfgr & SPR_CPUCFGR_ND);
+}
+
+void simulate_pc(struct pt_regs *regs, unsigned int jmp);
+void simulate_branch(struct pt_regs *regs, unsigned int jmp, bool has_delay_slot);
#endif /* __ASM_OPENRISC_INSN_DEF_H */
diff --git a/arch/openrisc/include/asm/spr_defs.h b/arch/openrisc/include/asm/spr_defs.h
index f0b6b492e9f4..5d13a9b96263 100644
--- a/arch/openrisc/include/asm/spr_defs.h
+++ b/arch/openrisc/include/asm/spr_defs.h
@@ -179,6 +179,7 @@
#define SPR_CPUCFGR_OF32S 0x00000080 /* ORFPX32 supported */
#define SPR_CPUCFGR_OF64S 0x00000100 /* ORFPX64 supported */
#define SPR_CPUCFGR_OV64S 0x00000200 /* ORVDX64 supported */
+#define SPR_CPUCFGR_ND 0x00000400 /* No delay slot */
#define SPR_CPUCFGR_RES 0xfffffc00 /* Reserved */
/*
diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile
index 19e0eb94f2eb..150779fbf010 100644
--- a/arch/openrisc/kernel/Makefile
+++ b/arch/openrisc/kernel/Makefile
@@ -5,7 +5,7 @@
always-$(KBUILD_BUILTIN) := vmlinux.lds
-obj-y := head.o setup.o or32_ksyms.o process.o dma.o \
+obj-y := head.o insn.o setup.o or32_ksyms.o process.o dma.o \
traps.o time.o irq.o entry.o ptrace.o signal.o \
sys_call_table.o unwinder.o cacheinfo.o
diff --git a/arch/openrisc/kernel/insn.c b/arch/openrisc/kernel/insn.c
new file mode 100644
index 000000000000..2c97eceee6d7
--- /dev/null
+++ b/arch/openrisc/kernel/insn.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * OpenRISC instruction utils
+ *
+ * Linux architectural port borrowing liberally from similar works of
+ * others. All original copyrights apply as per the original source
+ * declaration.
+ *
+ * OpenRISC implementation:
+ * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
+ */
+
+#include <linux/ptrace.h>
+#include <asm/insn-def.h>
+
+void simulate_pc(struct pt_regs *regs, unsigned int jmp)
+{
+ int displacement;
+ unsigned int rd, op;
+
+ displacement = sign_extend32(((jmp) & 0x7ffff) << 13, 31);
+ rd = (jmp & 0x3ffffff) >> 21;
+ op = jmp >> 26;
+
+ switch (op) {
+ case l_adrp:
+ regs->gpr[rd] = displacement + (regs->pc & (-8192));
+ return;
+ default:
+ break;
+ }
+}
+
+void simulate_branch(struct pt_regs *regs, unsigned int jmp_insn, bool has_delay_slot)
+{
+ int displacement;
+ unsigned int rb, op, jmp;
+
+ displacement = sign_extend32(((jmp_insn) & 0x3ffffff) << 2, 27);
+ rb = (jmp_insn & 0x0000ffff) >> 11;
+ op = jmp_insn >> 26;
+ jmp = has_delay_slot ? 2 * OPENRISC_INSN_SIZE : OPENRISC_INSN_SIZE;
+
+ switch (op) {
+ case l_j: /* l.j */
+ regs->pc += displacement;
+ return;
+ case l_jal: /* l.jal */
+ regs->gpr[9] = regs->pc + jmp;
+ regs->pc += displacement;
+ return;
+ case l_bnf: /* l.bnf */
+ if (regs->sr & SPR_SR_F)
+ regs->pc += jmp;
+ else
+ regs->pc += displacement;
+ return;
+ case l_bf: /* l.bf */
+ if (regs->sr & SPR_SR_F)
+ regs->pc += displacement;
+ else
+ regs->pc += jmp;
+ return;
+ case l_jr: /* l.jr */
+ regs->pc = regs->gpr[rb];
+ return;
+ case l_jalr: /* l.jalr */
+ regs->gpr[9] = regs->pc + jmp;
+ regs->pc = regs->gpr[rb];
+ return;
+ default:
+ break;
+ }
+}
diff --git a/arch/openrisc/kernel/jump_label.c b/arch/openrisc/kernel/jump_label.c
index ab7137c23b46..fe082eb847a4 100644
--- a/arch/openrisc/kernel/jump_label.c
+++ b/arch/openrisc/kernel/jump_label.c
@@ -34,7 +34,7 @@ bool arch_jump_label_transform_queue(struct jump_entry *entry,
insn = offset;
} else {
- insn = OPENRISC_INSN_NOP;
+ insn = INSN_NOP;
}
if (early_boot_irqs_disabled)
diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c
index c195be9cc9fc..ee87a3af34fc 100644
--- a/arch/openrisc/kernel/traps.c
+++ b/arch/openrisc/kernel/traps.c
@@ -32,6 +32,7 @@
#include <asm/bug.h>
#include <asm/fpu.h>
+#include <asm/insn-def.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/unwinder.h>
@@ -269,47 +270,9 @@ static inline int in_delay_slot(struct pt_regs *regs)
static inline void adjust_pc(struct pt_regs *regs, unsigned long address)
{
- int displacement;
- unsigned int rb, op, jmp;
-
if (unlikely(in_delay_slot(regs))) {
/* In delay slot, instruction at pc is a branch, simulate it */
- jmp = *((unsigned int *)regs->pc);
-
- displacement = sign_extend32(((jmp) & 0x3ffffff) << 2, 27);
- rb = (jmp & 0x0000ffff) >> 11;
- op = jmp >> 26;
-
- switch (op) {
- case 0x00: /* l.j */
- regs->pc += displacement;
- return;
- case 0x01: /* l.jal */
- regs->pc += displacement;
- regs->gpr[9] = regs->pc + 8;
- return;
- case 0x03: /* l.bnf */
- if (regs->sr & SPR_SR_F)
- regs->pc += 8;
- else
- regs->pc += displacement;
- return;
- case 0x04: /* l.bf */
- if (regs->sr & SPR_SR_F)
- regs->pc += displacement;
- else
- regs->pc += 8;
- return;
- case 0x11: /* l.jr */
- regs->pc = regs->gpr[rb];
- return;
- case 0x12: /* l.jalr */
- regs->pc = regs->gpr[rb];
- regs->gpr[9] = regs->pc + 8;
- return;
- default:
- break;
- }
+ simulate_branch(regs, *((unsigned int *)regs->pc), has_delay_slot());
} else {
regs->pc += 4;
}
--
2.53.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [RFC 2/2] openrisc: Add KProbes
2026-04-07 18:56 [RFC 0/2] openrisc: Add support for KProbes Sahil Siddiq
2026-04-07 18:56 ` [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
@ 2026-04-07 18:56 ` Sahil Siddiq
2026-04-15 6:48 ` [RFC 0/2] openrisc: Add support for KProbes Masami Hiramatsu
2 siblings, 0 replies; 15+ messages in thread
From: Sahil Siddiq @ 2026-04-07 18:56 UTC (permalink / raw)
To: jonas, stefan.kristiansson, shorne, naveen, davem, mhiramat
Cc: peterz, jpoimboe, jbaron, rostedt, ardb, chenmiao.ku, johannes,
nsc, masahiroy, tytso, linux-openrisc, linux-kernel,
linux-trace-kernel, Sahil Siddiq
Add KProbes support for OpenRISC. This work is primarily based
on similar work done for LoongArch, MIPS and RISC-V.
KProbes make it possible to trap at almost any address in the
kernel to collect performance/debugging info.
Signed-off-by: Sahil Siddiq <sahilcdq0@gmail.com>
---
arch/openrisc/Kconfig | 1 +
arch/openrisc/configs/or1ksim_defconfig | 2 +
arch/openrisc/configs/virt_defconfig | 2 +
arch/openrisc/include/asm/asm.h | 22 ++
arch/openrisc/include/asm/break.h | 19 ++
arch/openrisc/include/asm/kprobes.h | 76 +++++
arch/openrisc/kernel/Makefile | 1 +
arch/openrisc/kernel/entry.S | 16 +
arch/openrisc/kernel/kprobes.c | 381 ++++++++++++++++++++++++
arch/openrisc/kernel/traps.c | 26 ++
arch/openrisc/lib/memcpy.c | 2 +
arch/openrisc/lib/memset.S | 4 +
arch/openrisc/mm/fault.c | 5 +
samples/kprobes/kprobe_example.c | 8 +
14 files changed, 565 insertions(+)
create mode 100644 arch/openrisc/include/asm/asm.h
create mode 100644 arch/openrisc/include/asm/break.h
create mode 100644 arch/openrisc/include/asm/kprobes.h
create mode 100644 arch/openrisc/kernel/kprobes.c
diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig
index 9156635dd264..d240533b424b 100644
--- a/arch/openrisc/Kconfig
+++ b/arch/openrisc/Kconfig
@@ -27,6 +27,7 @@ config OPENRISC
select HAVE_ARCH_JUMP_LABEL
select HAVE_ARCH_JUMP_LABEL_RELATIVE
select HAVE_PCI
+ select HAVE_KPROBES
select HAVE_UID16
select HAVE_PAGE_SIZE_8KB
select HAVE_REGS_AND_STACK_ACCESS_API
diff --git a/arch/openrisc/configs/or1ksim_defconfig b/arch/openrisc/configs/or1ksim_defconfig
index 769705ac24d5..24d2915e7609 100644
--- a/arch/openrisc/configs/or1ksim_defconfig
+++ b/arch/openrisc/configs/or1ksim_defconfig
@@ -10,7 +10,9 @@ CONFIG_EXPERT=y
# CONFIG_KALLSYMS is not set
CONFIG_BUILTIN_DTB_NAME="or1ksim"
CONFIG_HZ_100=y
+CONFIG_OPENRISC=y
CONFIG_JUMP_LABEL=y
+CONFIG_KPROBES=y
CONFIG_MODULES=y
# CONFIG_BLOCK is not set
CONFIG_SLUB_TINY=y
diff --git a/arch/openrisc/configs/virt_defconfig b/arch/openrisc/configs/virt_defconfig
index 0b9979b35ca8..2eccb506032f 100644
--- a/arch/openrisc/configs/virt_defconfig
+++ b/arch/openrisc/configs/virt_defconfig
@@ -11,8 +11,10 @@ CONFIG_OPENRISC_HAVE_INST_SEXT=y
CONFIG_NR_CPUS=8
CONFIG_SMP=y
CONFIG_HZ_100=y
+CONFIG_OPENRISC=y
# CONFIG_OPENRISC_NO_SPR_SR_DSX is not set
CONFIG_JUMP_LABEL=y
+CONFIG_KPROBES=y
# CONFIG_COMPAT_BRK is not set
CONFIG_NET=y
CONFIG_PACKET=y
diff --git a/arch/openrisc/include/asm/asm.h b/arch/openrisc/include/asm/asm.h
new file mode 100644
index 000000000000..1a9c8bbb4430
--- /dev/null
+++ b/arch/openrisc/include/asm/asm.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Macros for OpenRISC asm
+ *
+ * Linux architectural port borrowing nearly verbatim from
+ * LoongArch and Arm. All original copyrights apply as per
+ * the original source declaration.
+ */
+
+#ifndef __ASM_ASM_H
+#define __ASM_ASM_H
+
+#ifdef CONFIG_KPROBES
+#define _ASM_NOKPROBE(symbol) \
+ .pushsection "_kprobe_blacklist", "aw"; \
+ .long symbol; \
+ .popsection
+#else
+#define _ASM_NOKPROBE(symbol)
+#endif
+
+#endif /* __ASM_ASM_H */
diff --git a/arch/openrisc/include/asm/break.h b/arch/openrisc/include/asm/break.h
new file mode 100644
index 000000000000..4bd04f4dd17a
--- /dev/null
+++ b/arch/openrisc/include/asm/break.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * OpenRISC trap codes used internally by the kernel
+ *
+ * Linux architectural port borrowing liberally from similar works of
+ * others. All original copyrights apply as per the original source
+ * declaration.
+ *
+ * Modifications for the OpenRISC architecture:
+ * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
+ */
+
+#ifndef __ASM_BREAK_H
+#define __ASM_BREAK_H
+
+#define BRK_KPROBE_BP 512 /* Kprobe break */
+#define BRK_KPROBE_SSTEPBP 1024 /* Kprobe single-step software implementation */
+
+#endif /* __ASM_BREAK_H */
diff --git a/arch/openrisc/include/asm/kprobes.h b/arch/openrisc/include/asm/kprobes.h
new file mode 100644
index 000000000000..50b6dc6d5a0c
--- /dev/null
+++ b/arch/openrisc/include/asm/kprobes.h
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * OpenRISC Linux
+ *
+ * Linux architectural port borrowing liberally from similar works of
+ * others. All original copyrights apply as per the original source
+ * declaration.
+ *
+ * OpenRISC implementation:
+ * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
+ */
+
+#ifndef __ASM_OPENRISC_KPROBES_H
+#define __ASM_OPENRISC_KPROBES_H
+
+#include <asm-generic/kprobes.h>
+
+#ifdef CONFIG_KPROBES
+#include <asm/break.h>
+#include <asm/cacheflush.h>
+
+#define __ARCH_WANT_KPROBES_INSN_SLOT
+
+struct pt_regs;
+struct kprobe;
+
+typedef u32 kprobe_opcode_t;
+
+/*
+ * MAX_INSN_SIZE is used as the number of slots in an executable
+ * page for single-stepping out of line (SSOL). We need two slots
+ * since we single-step using software breakpoints. The probed
+ * instruction is placed in the first slot with a breakpoint
+ * instruction in the second slot.
+ */
+#define MAX_INSN_SIZE 2
+
+/* Architecture specific copy of original instruction */
+struct arch_specific_insn {
+ /* copy of original instruction */
+ kprobe_opcode_t *insn;
+ /* address of next instruction in case of SSOL */
+ unsigned long restore;
+};
+
+struct prev_kprobe {
+ struct kprobe *kp;
+ unsigned int status;
+};
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+ unsigned int kprobe_status;
+ unsigned long irq_flags;
+ struct prev_kprobe prev_kprobe;
+};
+
+#define flush_insn_slot(p) do { } while (0)
+#define kretprobe_blacklist_size 0
+
+void arch_remove_kprobe(struct kprobe *p);
+int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
+bool kprobe_breakpoint_handler(struct pt_regs *regs);
+bool kprobe_singlestep_handler(struct pt_regs *regs);
+#else /* !CONFIG_KPROBES */
+static inline bool kprobe_breakpoint_handler(struct pt_regs *regs)
+{
+ return false;
+}
+
+static inline bool kprobe_singlestep_handler(struct pt_regs *regs)
+{
+ return false;
+}
+#endif /* CONFIG_KPROBES */
+#endif /* __ASM_OPENRISC_KPROBES_H */
diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile
index 150779fbf010..2ac824867963 100644
--- a/arch/openrisc/kernel/Makefile
+++ b/arch/openrisc/kernel/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SMP) += smp.o sync-timer.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_OF) += prom.o
+obj-$(CONFIG_KPROBES) += kprobes.o
obj-y += patching.o
clean:
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S
index c7e90b09645e..cd28bf1f7a3b 100644
--- a/arch/openrisc/kernel/entry.S
+++ b/arch/openrisc/kernel/entry.S
@@ -15,6 +15,7 @@
#include <linux/linkage.h>
#include <linux/pgtable.h>
+#include <asm/asm.h>
#include <asm/processor.h>
#include <asm/unistd.h>
#include <asm/thread_info.h>
@@ -640,6 +641,7 @@ ENTRY(_sys_call_handler)
/* r30 is the only register we clobber in the fast path */
/* r30 already saved */
/* l.sw PT_GPR30(r1),r30 */
+_ASM_NOKPROBE(_sys_call_handler)
_syscall_check_trace_enter:
/* syscalls run with interrupts enabled */
@@ -652,12 +654,14 @@ _syscall_check_trace_enter:
l.sfne r30,r0
l.bf _syscall_trace_enter
l.nop
+_ASM_NOKPROBE(_syscall_check_trace_enter)
_syscall_check:
/* Ensure that the syscall number is reasonable */
l.sfgeui r11,__NR_syscalls
l.bf _syscall_badsys
l.nop
+_ASM_NOKPROBE(_syscall_check)
_syscall_call:
l.movhi r29,hi(sys_call_table)
@@ -668,12 +672,14 @@ _syscall_call:
l.jalr r29
l.nop
+_ASM_NOKPROBE(_syscall_call)
_syscall_return:
/* All syscalls return here... just pay attention to ret_from_fork
* which does it in a round-about way.
*/
l.sw PT_GPR11(r1),r11 // save return value
+_ASM_NOKPROBE(_syscall_return)
#if 0
_syscall_debug:
@@ -708,6 +714,7 @@ _syscall_check_trace_leave:
l.sfne r30,r0
l.bf _syscall_trace_leave
l.nop
+_ASM_NOKPROBE(_syscall_check_trace_leave)
/* This is where the exception-return code begins... interrupts need to be
* disabled the rest of the way here because we can't afford to miss any
@@ -744,6 +751,7 @@ _syscall_check_work:
/* _work_pending needs to be called with interrupts disabled */
l.j _work_pending
l.nop
+_ASM_NOKPROBE(_syscall_check_work)
_syscall_resume_userspace:
// ENABLE_INTERRUPTS(r29)
@@ -800,6 +808,7 @@ _syscall_resume_userspace:
l.mtspr r0,r13,SPR_EPCR_BASE
l.mtspr r0,r15,SPR_ESR_BASE
l.rfe
+_ASM_NOKPROBE(_syscall_resume_userspace)
/* End of hot path!
* Keep the below tracing and error handling out of the hot path...
@@ -829,6 +838,7 @@ _syscall_trace_enter:
l.j _syscall_check
l.lwz r8,PT_GPR8(r1)
+_ASM_NOKPROBE(_syscall_trace_enter)
_syscall_trace_leave:
l.jal do_syscall_trace_leave
@@ -836,6 +846,7 @@ _syscall_trace_leave:
l.j _syscall_check_work
l.nop
+_ASM_NOKPROBE(_syscall_trace_leave)
_syscall_badsys:
/* Here we effectively pretend to have executed an imaginary
@@ -845,6 +856,7 @@ _syscall_badsys:
*/
l.j _syscall_return
l.addi r11,r0,-ENOSYS
+_ASM_NOKPROBE(_syscall_badsys)
/******* END SYSCALL HANDLING *******/
@@ -870,6 +882,7 @@ EXCEPTION_ENTRY(_trap_handler)
l.j _ret_from_exception
l.nop
+_ASM_NOKPROBE(_trap_handler)
/* ---[ 0xf00: Reserved exception ]-------------------------------------- */
@@ -1004,6 +1017,8 @@ ENTRY(_ret_from_exception)
l.nop
l.j _resume_userspace
l.nop
+_ASM_NOKPROBE(_ret_from_exception)
+_ASM_NOKPROBE(_ret_from_intr)
ENTRY(ret_from_fork)
l.jal schedule_tail
@@ -1038,6 +1053,7 @@ ENTRY(ret_from_fork)
l.j _syscall_return
l.nop
+_ASM_NOKPROBE(ret_from_fork)
/* ========================================================[ switch ] === */
diff --git a/arch/openrisc/kernel/kprobes.c b/arch/openrisc/kernel/kprobes.c
new file mode 100644
index 000000000000..f9073a1cb6eb
--- /dev/null
+++ b/arch/openrisc/kernel/kprobes.c
@@ -0,0 +1,381 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Kernel probes (KProbes) for OpenRISC
+ *
+ * Linux architectural port borrowing liberally from similar works of
+ * others. All original copyrights apply as per the original source
+ * declaration.
+ *
+ * OpenRISC implementation:
+ * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
+ */
+
+#include <linux/kprobes.h>
+#include <asm/insn-def.h>
+#include <asm/text-patching.h>
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe);
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+#define KPROBE_BP_INSN __emit_trap(BRK_KPROBE_BP)
+#define KPROBE_SSTEPBP_INSN __emit_trap(BRK_KPROBE_SSTEPBP)
+
+static bool insn_not_supported(union openrisc_instruction insn)
+{
+ switch (insn.word) {
+ case INSN_CSYNC:
+ case INSN_MSYNC:
+ case INSN_PSYNC:
+ return true;
+ }
+
+ if ((insn.word & 0xffff0000) == OPCODE_SYS)
+ return true;
+
+ if ((insn.word & 0xfc01ffff) == OPCODE_MACRC)
+ return true;
+
+ switch (insn.opcodes_6bit.opcode) {
+ case l_rfe:
+ case l_lwa:
+ case l_mfspr:
+ case l_mtspr:
+ case l_swa:
+ return true;
+ }
+
+ return false;
+}
+NOKPROBE_SYMBOL(insn_not_supported)
+
+static bool is_branch_insn(union openrisc_instruction insn)
+{
+ switch (insn.opcodes_6bit.opcode) {
+ case l_j:
+ case l_jal:
+ case l_bnf:
+ case l_bf:
+ case l_jr:
+ case l_jalr:
+ return true;
+ }
+
+ return false;
+}
+NOKPROBE_SYMBOL(is_branch_insn)
+
+static bool is_pc_insn(union openrisc_instruction insn)
+{
+ if (insn.opcodes_6bit.opcode == l_adrp)
+ return true;
+
+ return false;
+}
+NOKPROBE_SYMBOL(is_pc_insn)
+
+static bool insns_need_simulation(union openrisc_instruction insn, bool *exec_delay_slot)
+{
+ if (is_branch_insn(insn)) {
+ *exec_delay_slot = has_delay_slot();
+ return true;
+ }
+
+ if (is_pc_insn(insn)) {
+ *exec_delay_slot = false;
+ return true;
+ }
+
+ *exec_delay_slot = false;
+ return false;
+}
+NOKPROBE_SYMBOL(insns_need_simulation)
+
+int arch_prepare_kprobe(struct kprobe *p)
+{
+ union openrisc_instruction insn;
+ union openrisc_instruction prev_insn;
+ unsigned int cpucfgr = mfspr(SPR_CPUCFGR);
+ bool ss_delay_slot = false, exec_delay_slot = false;
+
+ /* Attempt to probe at unaligned address */
+ if ((unsigned long)p->addr & 0x3)
+ return -EILSEQ;
+
+ p->opcode = *p->addr;
+ insn.word = p->opcode;
+
+ if (insn_not_supported(insn)) {
+ pr_notice("Can't insert KProbe at blacklisted instruction.\n");
+ return -EINVAL;
+ }
+
+ if (copy_from_kernel_nofault(&prev_insn, p->addr - 1,
+ OPENRISC_INSN_SIZE) == 0 &&
+ insns_need_simulation(prev_insn, &exec_delay_slot) && exec_delay_slot) {
+ pr_notice("Can't insert KProbe in delay slot.\n");
+ return -EINVAL;
+ }
+
+ if (insns_need_simulation(insn, &exec_delay_slot) && !exec_delay_slot) {
+ p->ainsn.insn = NULL;
+ p->ainsn.restore = 0;
+ } else {
+ /*
+ * Single step probed instruction or, in case of branch instructions, single
+ * step instruction in delay slot.
+ */
+ p->ainsn.insn = get_insn_slot();
+ if (!p->ainsn.insn)
+ return -ENOMEM;
+
+ if (exec_delay_slot) {
+ patch_insn_write(p->ainsn.insn, *(p->addr + 1));
+ p->ainsn.restore = 0;
+ } else {
+ patch_insn_write(p->ainsn.insn, p->opcode);
+ p->ainsn.restore = (unsigned long)p->addr + OPENRISC_INSN_SIZE;
+ }
+ patch_insn_write(&p->ainsn.insn[1], KPROBE_SSTEPBP_INSN);
+ }
+
+ return 0;
+}
+NOKPROBE_SYMBOL(arch_prepare_kprobe);
+
+void arch_arm_kprobe(struct kprobe *p)
+{
+ patch_insn_write(p->addr, KPROBE_BP_INSN);
+ flush_insn_slot(p);
+}
+NOKPROBE_SYMBOL(arch_arm_kprobe);
+
+void arch_disarm_kprobe(struct kprobe *p)
+{
+ patch_insn_write(p->addr, p->opcode);
+ flush_insn_slot(p);
+}
+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 set_current_kprobe(struct kprobe *p)
+{
+ __this_cpu_write(current_kprobe, p);
+}
+NOKPROBE_SYMBOL(set_current_kprobe);
+
+static void save_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ kcb->prev_kprobe.kp = kprobe_running();
+ kcb->prev_kprobe.status = kcb->kprobe_status;
+}
+NOKPROBE_SYMBOL(save_previous_kprobe);
+
+static void restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ kcb->kprobe_status = kcb->prev_kprobe.status;
+ set_current_kprobe(kcb->prev_kprobe.kp);
+}
+NOKPROBE_SYMBOL(restore_previous_kprobe);
+
+static void post_kprobe_handler(struct kprobe *cur, struct kprobe_ctlblk *kcb,
+ struct pt_regs *regs)
+{
+ if (cur->ainsn.restore != 0)
+ instruction_pointer_set(regs, (unsigned long)cur->ainsn.restore);
+
+ if (kcb->kprobe_status == KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ preempt_enable_no_resched();
+ return;
+ }
+
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+
+ if (cur->post_handler)
+ cur->post_handler(cur, regs, 0);
+
+ reset_current_kprobe();
+ preempt_enable_no_resched();
+}
+NOKPROBE_SYMBOL(post_kprobe_handler);
+
+static void setup_singlestep(struct kprobe *p, struct pt_regs *regs,
+ struct kprobe_ctlblk *kcb, int reenter)
+{
+ union openrisc_instruction insn;
+
+ if (reenter) {
+ save_previous_kprobe(kcb);
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_REENTER;
+ } else {
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ }
+
+ /* Emulate instruction if required. */
+ insn.word = p->opcode;
+ if (is_branch_insn(insn)) {
+ simulate_branch(regs, insn.word, has_delay_slot());
+ /* Save target addr before updating PC to SSOL slot */
+ p->ainsn.restore = regs->pc;
+ } else if (is_pc_insn(insn)) {
+ simulate_pc(regs, insn.word);
+ /* No SSOL is required here, so call post-process immediately. */
+ post_kprobe_handler(p, kcb, regs);
+ return;
+ }
+
+ if (p->ainsn.insn) {
+ /* Disable IRQs before single stepping */
+ local_irq_save(kcb->irq_flags);
+ instruction_pointer_set(regs, (unsigned long)p->ainsn.insn);
+ } else {
+ /*
+ * The instruction is a branch but delay slots are disabled.
+ * Simply call the post-handler.
+ */
+ post_kprobe_handler(p, kcb, regs);
+ }
+}
+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_SS:
+ case KPROBE_HIT_SSDONE:
+ case KPROBE_HIT_ACTIVE:
+ kprobes_inc_nmissed_count(p);
+ setup_singlestep(p, regs, kcb, 1);
+ break;
+ case KPROBE_REENTER:
+ pr_warn("Unrecoverable KProbe detected.\n");
+ dump_kprobe(p);
+ WARN_ON_ONCE(1);
+ break;
+ default:
+ WARN_ON(1);
+ return false;
+ }
+
+ return true;
+}
+NOKPROBE_SYMBOL(reenter_kprobe);
+
+bool kprobe_breakpoint_handler(struct pt_regs *regs)
+{
+ struct kprobe *p, *curr_kprobe;
+ struct kprobe_ctlblk *kcb;
+ kprobe_opcode_t *addr = (kprobe_opcode_t *)regs->pc;
+
+ /*
+ * We don't want to be preempted for the entire
+ * duration of kprobe processing.
+ */
+ preempt_disable();
+
+ kcb = get_kprobe_ctlblk();
+ curr_kprobe = kprobe_running();
+ p = get_kprobe(addr);
+ if (p) {
+ if (curr_kprobe) {
+ if (reenter_kprobe(p, regs, kcb))
+ return true;
+ } else {
+ /* Probe hit */
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+ /*
+ * If there's no pre-handler or it returns 0, continue
+ * processing the KProbe as usual. A non-zero return
+ * value implies the saved registers have been modified
+ * and the execution path might deviate from what's
+ * expected. Reset the KProbe and return.
+ */
+ if (!p->pre_handler || !p->pre_handler(p, regs)) {
+ setup_singlestep(p, regs, kcb, 0);
+ } else {
+ reset_current_kprobe();
+ preempt_enable_no_resched();
+ }
+ return true;
+ }
+ }
+
+ /*
+ * The breakpoint instruction was removed right after we hit it
+ * possibly by another cpu. If the original instruction was also
+ * a trap instruction then return to the trap handler for further
+ * processing, else no further processing is required.
+ */
+ if ((*addr & 0xffff0000) != OPCODE_TRAP) {
+ preempt_enable_no_resched();
+ return true;
+ }
+
+ preempt_enable_no_resched();
+ return false;
+}
+NOKPROBE_SYMBOL(kprobe_breakpoint_handler);
+
+bool kprobe_singlestep_handler(struct pt_regs *regs)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ unsigned long addr = instruction_pointer(regs);
+
+ if (cur && (kcb->kprobe_status & (KPROBE_HIT_SS | KPROBE_REENTER)) &&
+ ((unsigned long)&cur->ainsn.insn[1] == addr)) {
+ /* Single stepping has been completed. Enable IRQs. */
+ local_irq_restore(kcb->irq_flags);
+ post_kprobe_handler(cur, kcb, regs);
+ return true;
+ }
+
+ preempt_enable_no_resched();
+ return false;
+}
+NOKPROBE_SYMBOL(kprobe_singlestep_handler);
+
+int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ /*
+ * The instruction being single-stepped caused a page fault.
+ * Reset the current KProbe and set PC to the probe's address.
+ * Then allow the page fault handler to continue as usual.
+ */
+ instruction_pointer_set(regs, (unsigned long)cur->addr);
+
+ if (kcb->kprobe_status == KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ } else {
+ local_irq_restore(kcb->irq_flags);
+ reset_current_kprobe();
+ preempt_enable_no_resched();
+ }
+ break;
+ }
+
+ return 0;
+}
+NOKPROBE_SYMBOL(kprobe_fault_handler);
+
+int __init arch_init_kprobes(void)
+{
+ return 0;
+}
diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c
index ee87a3af34fc..ae6bfcca388e 100644
--- a/arch/openrisc/kernel/traps.c
+++ b/arch/openrisc/kernel/traps.c
@@ -30,10 +30,12 @@
#include <linux/kallsyms.h>
#include <linux/uaccess.h>
+#include <asm/break.h>
#include <asm/bug.h>
#include <asm/fpu.h>
#include <asm/insn-def.h>
#include <asm/io.h>
+#include <asm/kprobes.h>
#include <asm/processor.h>
#include <asm/unwinder.h>
#include <asm/sections.h>
@@ -216,10 +218,34 @@ asmlinkage void do_trap(struct pt_regs *regs, unsigned long address)
if (user_mode(regs)) {
force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->pc);
} else {
+ unsigned int trap = *(unsigned int *)regs->pc, cpucfgr = mfspr(SPR_CPUCFGR), bcode;
+
+ /*
+ * Trap instruction was probably removed and no further processing
+ * is required.
+ */
+ if ((trap & 0xffff0000) != OPCODE_TRAP)
+ return;
+
+ bcode = (trap & 0xffff);
+ switch (bcode) {
+ case BRK_KPROBE_BP:
+ if (kprobe_breakpoint_handler(regs))
+ return;
+ break;
+ case BRK_KPROBE_SSTEPBP:
+ if (kprobe_singlestep_handler(regs))
+ return;
+ break;
+ default:
+ break;
+ }
+
pr_emerg("KERNEL: Illegal trap exception 0x%.8lx\n", regs->pc);
die("Die:", regs, SIGILL);
}
}
+NOKPROBE_SYMBOL(do_trap)
asmlinkage void do_unaligned_access(struct pt_regs *regs, unsigned long address)
{
diff --git a/arch/openrisc/lib/memcpy.c b/arch/openrisc/lib/memcpy.c
index e2af9b510804..701262a40134 100644
--- a/arch/openrisc/lib/memcpy.c
+++ b/arch/openrisc/lib/memcpy.c
@@ -16,6 +16,7 @@
#include <linux/export.h>
+#include <linux/kprobes.h>
#include <linux/string.h>
#ifdef CONFIG_OR1K_1200
@@ -122,4 +123,5 @@ void *memcpy(void *dest, __const void *src, __kernel_size_t n)
}
#endif
+NOKPROBE_SYMBOL(memcpy);
EXPORT_SYMBOL(memcpy);
diff --git a/arch/openrisc/lib/memset.S b/arch/openrisc/lib/memset.S
index c3ac2a8b68d3..14e63af12d73 100644
--- a/arch/openrisc/lib/memset.S
+++ b/arch/openrisc/lib/memset.S
@@ -9,6 +9,8 @@
* Copyright (C) 2015 Olof Kindgren <olof.kindgren@gmail.com>
*/
+#include <asm/asm.h>
+
.global memset
.type memset, @function
memset:
@@ -92,3 +94,5 @@ memset:
4: l.jr r9
l.ori r11, r3, 0
+
+_ASM_NOKPROBE(memset)
diff --git a/arch/openrisc/mm/fault.c b/arch/openrisc/mm/fault.c
index 29e232d78d82..5263a832562f 100644
--- a/arch/openrisc/mm/fault.c
+++ b/arch/openrisc/mm/fault.c
@@ -14,6 +14,7 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/extable.h>
+#include <linux/kprobes.h>
#include <linux/sched/signal.h>
#include <linux/perf_event.h>
@@ -55,6 +56,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address,
tsk = current;
+ if (kprobe_page_fault(regs, vector))
+ return;
+
/*
* We fault-in kernel-space virtual memory on-demand. The
* 'reference' page table is init_mm.pgd.
@@ -351,3 +355,4 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address,
return;
}
}
+NOKPROBE_SYMBOL(do_page_fault)
diff --git a/samples/kprobes/kprobe_example.c b/samples/kprobes/kprobe_example.c
index 53ec6c8b8c40..84e26ebef70b 100644
--- a/samples/kprobes/kprobe_example.c
+++ b/samples/kprobes/kprobe_example.c
@@ -59,6 +59,10 @@ static int __kprobes handler_pre(struct kprobe *p, struct pt_regs *regs)
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
+#ifdef CONFIG_OPENRISC
+ pr_info("<%s> p->addr = 0x%p, pc = 0x%lx, status = 0x%lx\n",
+ p->symbol_name, p->addr, regs->pc, regs->sr);
+#endif
/* A dump_stack() here will give a stack backtrace */
return 0;
@@ -100,6 +104,10 @@ static void __kprobes handler_post(struct kprobe *p, struct pt_regs *regs,
pr_info("<%s> p->addr = 0x%p, estat = 0x%lx\n",
p->symbol_name, p->addr, regs->csr_estat);
#endif
+#ifdef CONFIG_OPENRISC
+ pr_info("<%s> p->addr = 0x%p, status = 0x%lx\n",
+ p->symbol_name, p->addr, regs->sr);
+#endif
}
static int __init kprobe_init(void)
--
2.53.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions
2026-04-07 18:56 ` [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
@ 2026-04-14 17:11 ` Stafford Horne
2026-04-15 6:10 ` Sahil
2026-04-15 6:39 ` Masami Hiramatsu
2026-09-30 20:31 ` [PATCH v2 0/4] openrisc: Add support for KProbes Sahil Siddiq
` (4 subsequent siblings)
5 siblings, 2 replies; 15+ messages in thread
From: Stafford Horne @ 2026-04-14 17:11 UTC (permalink / raw)
To: Sahil Siddiq
Cc: jonas, stefan.kristiansson, naveen, davem, mhiramat, peterz,
jpoimboe, jbaron, rostedt, ardb, chenmiao.ku, johannes, nsc,
masahiroy, tytso, linux-openrisc, linux-kernel,
linux-trace-kernel
Hello Sahil,
Thanks for your patches. I have a few questions. so this is not really a
technical review at the moment just some main items.
On Wed, Apr 08, 2026 at 12:26:49AM +0530, Sahil Siddiq wrote:
> Introduce new instruction-related utilities and macros for OpenRISC.
> This is in preparation for patches that add tracing support such as
> KProbes.
>
> Simulate l.adrp. Fix bugs in simulation of l.jal and l.jalr. Earlier,
Why emulate l.adrp? We don't really use this yet in any OpenRISC code. This
instruction is meant to be used to help -fpic code, see:
https://openrisc.io/proposals/ladrp
> PC was being updated and then saved in the link register r9, resulting
> in a corrupted page table (bad page map in process). Instead, update
> PC after storing it in r9.
>
> Move instruction simulation to its own file to enable reuse. Clean it
> up and replace hardcoded values with computed expressions.
>
> Link: https://raw.githubusercontent.com/openrisc/doc/master/openrisc-arch-1.4-rev0.pdf
> Signed-off-by: Sahil Siddiq <sahilcdq0@gmail.com>
> ---
> arch/openrisc/include/asm/insn-def.h | 61 +++++++++++++++++++++--
> arch/openrisc/include/asm/spr_defs.h | 1 +
> arch/openrisc/kernel/Makefile | 2 +-
> arch/openrisc/kernel/insn.c | 74 ++++++++++++++++++++++++++++
> arch/openrisc/kernel/jump_label.c | 2 +-
> arch/openrisc/kernel/traps.c | 41 +--------------
> 6 files changed, 136 insertions(+), 45 deletions(-)
> create mode 100644 arch/openrisc/kernel/insn.c
>
> diff --git a/arch/openrisc/include/asm/insn-def.h b/arch/openrisc/include/asm/insn-def.h
> index 1e0c028a5b95..c98f9770c52e 100644
> --- a/arch/openrisc/include/asm/insn-def.h
> +++ b/arch/openrisc/include/asm/insn-def.h
> @@ -3,13 +3,66 @@
> * Copyright (C) 2025 Chen Miao
> */
>
> +#include <asm/spr.h>
> +#include <asm/spr_defs.h>
> +
> #ifndef __ASM_OPENRISC_INSN_DEF_H
> #define __ASM_OPENRISC_INSN_DEF_H
>
> -/* or1k instructions are always 32 bits. */
> -#define OPENRISC_INSN_SIZE 4
> -
> /* or1k nop instruction code */
> -#define OPENRISC_INSN_NOP 0x15000000U
> +#define INSN_NOP 0x15000000U
Why remove the OPENRISC_ namespace from thse definitions?
Other architectures like arm64, riscv have namespaces on these similar
definitions.
> +
> +#define INSN_CSYNC 0x23000000U
> +#define INSN_MSYNC 0x22000000U
> +#define INSN_PSYNC 0x22800000U
> +
> +#define OPCODE_TRAP 0x21000000U
> +#define OPCODE_SYS 0x20000000U
> +#define OPCODE_MACRC 0x18010000U
> +
> +struct pt_regs;
> +
> +enum six_bit_opcodes {
I don't see why we need to name this six_bit as such. OpenRISC
instructions have either fir most significant 6-bits or 11-bits
as opcodes but we still use the first 6-bits to drive the logic.
> + l_rfe = 0x09,
> + l_lwa = 0x1b,
> + l_mfspr = 0x2d,
> + l_mtspr = 0x30,
> + l_swa = 0x33,
> + l_j = 0x00,
> + l_jal = 0x01,
> + l_adrp = 0x02,
> + l_bnf = 0x03,
> + l_bf = 0x04,
> + l_jr = 0x11,
> + l_jalr = 0x12,
> +};
> +
> +struct insn {
> + unsigned int opcode: 6;
> + unsigned int operands: 26;
> +};
> +
> +union openrisc_instruction {
> + unsigned int word;
> + struct insn opcodes_6bit;
> +};
> +
> +#define OPENRISC_INSN_SIZE (sizeof(union openrisc_instruction))
> +
> +/* Helpers for working with l.trap */
> +static inline unsigned long __emit_trap(unsigned int code)
> +{
> + return (code & 0xffff) | OPCODE_TRAP;
> +}
> +
> +static inline bool has_delay_slot(void)
> +{
> + unsigned int cpucfgr = mfspr(SPR_CPUCFGR);
> +
> + return !(cpucfgr & SPR_CPUCFGR_ND);
> +}
This is for handling CPU's that do not have delay slots. We didn't do this
before, why are you doing it now? Should we mention this in the git commit
message?
Also, since this is a static configuration for the CPU should we use static keys
for this?
> +
> +void simulate_pc(struct pt_regs *regs, unsigned int jmp);
> +void simulate_branch(struct pt_regs *regs, unsigned int jmp, bool has_delay_slot);
>
> #endif /* __ASM_OPENRISC_INSN_DEF_H */
> diff --git a/arch/openrisc/include/asm/spr_defs.h b/arch/openrisc/include/asm/spr_defs.h
> index f0b6b492e9f4..5d13a9b96263 100644
> --- a/arch/openrisc/include/asm/spr_defs.h
> +++ b/arch/openrisc/include/asm/spr_defs.h
> @@ -179,6 +179,7 @@
> #define SPR_CPUCFGR_OF32S 0x00000080 /* ORFPX32 supported */
> #define SPR_CPUCFGR_OF64S 0x00000100 /* ORFPX64 supported */
> #define SPR_CPUCFGR_OV64S 0x00000200 /* ORVDX64 supported */
> +#define SPR_CPUCFGR_ND 0x00000400 /* No delay slot */
> #define SPR_CPUCFGR_RES 0xfffffc00 /* Reserved */
>
> /*
> diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile
> index 19e0eb94f2eb..150779fbf010 100644
> --- a/arch/openrisc/kernel/Makefile
> +++ b/arch/openrisc/kernel/Makefile
> @@ -5,7 +5,7 @@
>
> always-$(KBUILD_BUILTIN) := vmlinux.lds
>
> -obj-y := head.o setup.o or32_ksyms.o process.o dma.o \
> +obj-y := head.o insn.o setup.o or32_ksyms.o process.o dma.o \
> traps.o time.o irq.o entry.o ptrace.o signal.o \
> sys_call_table.o unwinder.o cacheinfo.o
>
> diff --git a/arch/openrisc/kernel/insn.c b/arch/openrisc/kernel/insn.c
> new file mode 100644
> index 000000000000..2c97eceee6d7
> --- /dev/null
> +++ b/arch/openrisc/kernel/insn.c
> @@ -0,0 +1,74 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * OpenRISC instruction utils
> + *
> + * Linux architectural port borrowing liberally from similar works of
> + * others. All original copyrights apply as per the original source
> + * declaration.
> + *
> + * OpenRISC implementation:
> + * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
> + */
> +
> +#include <linux/ptrace.h>
> +#include <asm/insn-def.h>
> +
> +void simulate_pc(struct pt_regs *regs, unsigned int jmp)
> +{
> + int displacement;
> + unsigned int rd, op;
> +
> + displacement = sign_extend32(((jmp) & 0x7ffff) << 13, 31);
> + rd = (jmp & 0x3ffffff) >> 21;
> + op = jmp >> 26;
> +
> + switch (op) {
> + case l_adrp:
> + regs->gpr[rd] = displacement + (regs->pc & (-8192));
> + return;
> + default:
> + break;
> + }
> +}
This function is not used, what will it be used for? Could you mention it in the
commit message? Also could you document it?
> +
> +void simulate_branch(struct pt_regs *regs, unsigned int jmp_insn, bool has_delay_slot)
Since you are using this for more than just instruction simulation now, maybe
its good to document what it does. For example when we use it below we surround
it with the in_delay_slot check.
> +{
> + int displacement;
> + unsigned int rb, op, jmp;
> +
> + displacement = sign_extend32(((jmp_insn) & 0x3ffffff) << 2, 27);
> + rb = (jmp_insn & 0x0000ffff) >> 11;
> + op = jmp_insn >> 26;
Naming this variable jmp is a bit misleading. Maybe something like
link_displacement, link_offset or fallthrough?
> + jmp = has_delay_slot ? 2 * OPENRISC_INSN_SIZE : OPENRISC_INSN_SIZE;
> +
> + switch (op) {
> + case l_j: /* l.j */
> + regs->pc += displacement;
> + return;
> + case l_jal: /* l.jal */
> + regs->gpr[9] = regs->pc + jmp;
> + regs->pc += displacement;
> + return;
> + case l_bnf: /* l.bnf */
> + if (regs->sr & SPR_SR_F)
> + regs->pc += jmp;
> + else
> + regs->pc += displacement;
> + return;
> + case l_bf: /* l.bf */
> + if (regs->sr & SPR_SR_F)
> + regs->pc += displacement;
> + else
> + regs->pc += jmp;
> + return;
> + case l_jr: /* l.jr */
> + regs->pc = regs->gpr[rb];
> + return;
> + case l_jalr: /* l.jalr */
> + regs->gpr[9] = regs->pc + jmp;
> + regs->pc = regs->gpr[rb];
> + return;
> + default:
> + break;
> + }
> +}
> diff --git a/arch/openrisc/kernel/jump_label.c b/arch/openrisc/kernel/jump_label.c
> index ab7137c23b46..fe082eb847a4 100644
> --- a/arch/openrisc/kernel/jump_label.c
> +++ b/arch/openrisc/kernel/jump_label.c
> @@ -34,7 +34,7 @@ bool arch_jump_label_transform_queue(struct jump_entry *entry,
>
> insn = offset;
> } else {
> - insn = OPENRISC_INSN_NOP;
> + insn = INSN_NOP;
> }
>
> if (early_boot_irqs_disabled)
> diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c
> index c195be9cc9fc..ee87a3af34fc 100644
> --- a/arch/openrisc/kernel/traps.c
> +++ b/arch/openrisc/kernel/traps.c
> @@ -32,6 +32,7 @@
>
> #include <asm/bug.h>
> #include <asm/fpu.h>
> +#include <asm/insn-def.h>
> #include <asm/io.h>
> #include <asm/processor.h>
> #include <asm/unwinder.h>
> @@ -269,47 +270,9 @@ static inline int in_delay_slot(struct pt_regs *regs)
>
> static inline void adjust_pc(struct pt_regs *regs, unsigned long address)
> {
> - int displacement;
> - unsigned int rb, op, jmp;
> -
> if (unlikely(in_delay_slot(regs))) {
> /* In delay slot, instruction at pc is a branch, simulate it */
> - jmp = *((unsigned int *)regs->pc);
> -
> - displacement = sign_extend32(((jmp) & 0x3ffffff) << 2, 27);
> - rb = (jmp & 0x0000ffff) >> 11;
> - op = jmp >> 26;
> -
> - switch (op) {
> - case 0x00: /* l.j */
> - regs->pc += displacement;
> - return;
> - case 0x01: /* l.jal */
> - regs->pc += displacement;
> - regs->gpr[9] = regs->pc + 8;
> - return;
> - case 0x03: /* l.bnf */
> - if (regs->sr & SPR_SR_F)
> - regs->pc += 8;
> - else
> - regs->pc += displacement;
> - return;
> - case 0x04: /* l.bf */
> - if (regs->sr & SPR_SR_F)
> - regs->pc += displacement;
> - else
> - regs->pc += 8;
> - return;
> - case 0x11: /* l.jr */
> - regs->pc = regs->gpr[rb];
> - return;
> - case 0x12: /* l.jalr */
> - regs->pc = regs->gpr[rb];
> - regs->gpr[9] = regs->pc + 8;
> - return;
> - default:
> - break;
> - }
> + simulate_branch(regs, *((unsigned int *)regs->pc), has_delay_slot());
> } else {
> regs->pc += 4;
> }
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions
2026-04-14 17:11 ` Stafford Horne
@ 2026-04-15 6:10 ` Sahil
2026-04-15 6:39 ` Masami Hiramatsu
1 sibling, 0 replies; 15+ messages in thread
From: Sahil @ 2026-04-15 6:10 UTC (permalink / raw)
To: Stafford Horne
Cc: jonas, stefan.kristiansson, naveen, davem, mhiramat, peterz,
jpoimboe, jbaron, rostedt, ardb, chenmiao.ku, johannes, nsc,
masahiroy, tytso, linux-openrisc, linux-kernel,
linux-trace-kernel
Hi Stafford,
Thank you for your review.
On 4/14/26 10:41 PM, Stafford Horne wrote:
> Hello Sahil,
>
> Thanks for your patches. I have a few questions. so this is not really a
> technical review at the moment just some main items.
>
> On Wed, Apr 08, 2026 at 12:26:49AM +0530, Sahil Siddiq wrote:
>> Introduce new instruction-related utilities and macros for OpenRISC.
>> This is in preparation for patches that add tracing support such as
>> KProbes.
>>
>> Simulate l.adrp. Fix bugs in simulation of l.jal and l.jalr. Earlier,
>
> Why emulate l.adrp? We don't really use this yet in any OpenRISC code. This
> instruction is meant to be used to help -fpic code, see:
>
> https://openrisc.io/proposals/ladrp
Right, I couldn't find l.adrp in the kernel's image after compilation. But I
was thinking, in case l.adrp is used somewhere in the future (for e.g. in a
custom kernel module) and a probe is placed at that instruction, we would need
to emulate that instruction. This is because the target address is calculated
using the instruction's address (InsnAddr & -8192). If l.adrp is moved to an
out-of-line slot to single-step for KProbes, the slot's address will be used to
generate the target address instead of the original address.
Since l.adrp is not really used anywhere and it's highly unlikely that it will
be probed, an alternative could be to add l.adrp to the blacklist so that probes
at this instruction are never inserted.
>> PC was being updated and then saved in the link register r9, resulting
>> in a corrupted page table (bad page map in process). Instead, update
>> PC after storing it in r9.
>>
>> Move instruction simulation to its own file to enable reuse. Clean it
>> up and replace hardcoded values with computed expressions.
>>
>> Link: https://raw.githubusercontent.com/openrisc/doc/master/openrisc-arch-1.4-rev0.pdf
>> Signed-off-by: Sahil Siddiq <sahilcdq0@gmail.com>
>> ---
>> arch/openrisc/include/asm/insn-def.h | 61 +++++++++++++++++++++--
>> arch/openrisc/include/asm/spr_defs.h | 1 +
>> arch/openrisc/kernel/Makefile | 2 +-
>> arch/openrisc/kernel/insn.c | 74 ++++++++++++++++++++++++++++
>> arch/openrisc/kernel/jump_label.c | 2 +-
>> arch/openrisc/kernel/traps.c | 41 +--------------
>> 6 files changed, 136 insertions(+), 45 deletions(-)
>> create mode 100644 arch/openrisc/kernel/insn.c
>>
>> diff --git a/arch/openrisc/include/asm/insn-def.h b/arch/openrisc/include/asm/insn-def.h
>> index 1e0c028a5b95..c98f9770c52e 100644
>> --- a/arch/openrisc/include/asm/insn-def.h
>> +++ b/arch/openrisc/include/asm/insn-def.h
>> @@ -3,13 +3,66 @@
>> * Copyright (C) 2025 Chen Miao
>> */
>>
>> +#include <asm/spr.h>
>> +#include <asm/spr_defs.h>
>> +
>> #ifndef __ASM_OPENRISC_INSN_DEF_H
>> #define __ASM_OPENRISC_INSN_DEF_H
>>
>> -/* or1k instructions are always 32 bits. */
>> -#define OPENRISC_INSN_SIZE 4
>> -
>> /* or1k nop instruction code */
>> -#define OPENRISC_INSN_NOP 0x15000000U
>> +#define INSN_NOP 0x15000000U
>
> Why remove the OPENRISC_ namespace from thse definitions?
>
> Other architectures like arm64, riscv have namespaces on these similar
> definitions.
Oh, got it. I hadn't checked those architectures at that point. I was
following the convention in LoongArch and the namespace wasn't there. I'll
revert this change.
>> +
>> +#define INSN_CSYNC 0x23000000U
>> +#define INSN_MSYNC 0x22000000U
>> +#define INSN_PSYNC 0x22800000U
>> +
>> +#define OPCODE_TRAP 0x21000000U
>> +#define OPCODE_SYS 0x20000000U
>> +#define OPCODE_MACRC 0x18010000U
I'll add the namespace here.
>> +struct pt_regs;
>> +
>> +enum six_bit_opcodes {
>
> I don't see why we need to name this six_bit as such. OpenRISC
> instructions have either fir most significant 6-bits or 11-bits
> as opcodes but we still use the first 6-bits to drive the logic.
Maybe it should be renamed to something that indicates this is related
to probes (I wasn't able to come up with a better name).
The enums are used in a few places in KProbe's implementation to check
if an instruction requires emulation, has been blacklisted, or can be
single-stepped in an out-of-line slot. An alternative would be to use
macros (similar to OPCODE_SYS) but that won't have type checking. Either
way, I think it's better than using raw values in multiple places.
Shall I simply rename the enum type, or shall I switch to macros? What
are your thoughts?
>> + l_rfe = 0x09,
>> + l_lwa = 0x1b,
>> + l_mfspr = 0x2d,
>> + l_mtspr = 0x30,
>> + l_swa = 0x33,
>> + l_j = 0x00,
>> + l_jal = 0x01,
>> + l_adrp = 0x02,
>> + l_bnf = 0x03,
>> + l_bf = 0x04,
>> + l_jr = 0x11,
>> + l_jalr = 0x12,
>> +};
>> +
>> +struct insn {
>> + unsigned int opcode: 6;
>> + unsigned int operands: 26;
>> +};
>> +
>> +union openrisc_instruction {
>> + unsigned int word;
>> + struct insn opcodes_6bit;
>> +};
>> +
>> +#define OPENRISC_INSN_SIZE (sizeof(union openrisc_instruction))
>> +
>> +/* Helpers for working with l.trap */
>> +static inline unsigned long __emit_trap(unsigned int code)
>> +{
>> + return (code & 0xffff) | OPCODE_TRAP;
>> +}
>> +
>> +static inline bool has_delay_slot(void)
>> +{
>> + unsigned int cpucfgr = mfspr(SPR_CPUCFGR);
>> +
>> + return !(cpucfgr & SPR_CPUCFGR_ND);
>> +}
>
> This is for handling CPU's that do not have delay slots. We didn't do this
> before, why are you doing it now? Should we mention this in the git commit
> message?
I am not entirely sure why CPUs with no delay slot were not taken into account
initially. The "in_delay_slot" function in traps.c [1] always assumes (for the
case where SPR_SR_DSX is not implemented) that the CPU has a delay slot if PC is
a branch instruction.
Given that the specification allows CPUs not to have a delay slot, I thought I
would include that as well. With no SPR_SR_DSX, if it can be guaranteed that PC
will be a branch instruction only when delay slots are enabled, then there should
be no change in behaviour in "simulate_branch" even with the addition of the
"has_delay_slot" check.
However, if PC can be a branch instruction even when there are no delay slots, then
"simulate_branch" now adds 4 to regs->pc instead of adding 8 unconditionally. Please
correct me if I am wrong.
"has_delay_slot" is particularly important for KProbes, since a probe at a branch
instruction with a delay slot means the branch will have to be emulated followed
by single-stepping of the delay slot instruction.
But if there are no delay slots, then only branch emulation is required.
> Also, since this is a static configuration for the CPU should we use static keys
> for this?
Yes, I think static keys can be used here. If the more likely case is that a delay
slot exists, we can make that the more likely branch.
>> +
>> +void simulate_pc(struct pt_regs *regs, unsigned int jmp);
>> +void simulate_branch(struct pt_regs *regs, unsigned int jmp, bool has_delay_slot);
>>
>> #endif /* __ASM_OPENRISC_INSN_DEF_H */
>> diff --git a/arch/openrisc/include/asm/spr_defs.h b/arch/openrisc/include/asm/spr_defs.h
>> index f0b6b492e9f4..5d13a9b96263 100644
>> --- a/arch/openrisc/include/asm/spr_defs.h
>> +++ b/arch/openrisc/include/asm/spr_defs.h
>> @@ -179,6 +179,7 @@
>> #define SPR_CPUCFGR_OF32S 0x00000080 /* ORFPX32 supported */
>> #define SPR_CPUCFGR_OF64S 0x00000100 /* ORFPX64 supported */
>> #define SPR_CPUCFGR_OV64S 0x00000200 /* ORVDX64 supported */
>> +#define SPR_CPUCFGR_ND 0x00000400 /* No delay slot */
>> #define SPR_CPUCFGR_RES 0xfffffc00 /* Reserved */
>>
>> /*
>> diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile
>> index 19e0eb94f2eb..150779fbf010 100644
>> --- a/arch/openrisc/kernel/Makefile
>> +++ b/arch/openrisc/kernel/Makefile
>> @@ -5,7 +5,7 @@
>>
>> always-$(KBUILD_BUILTIN) := vmlinux.lds
>>
>> -obj-y := head.o setup.o or32_ksyms.o process.o dma.o \
>> +obj-y := head.o insn.o setup.o or32_ksyms.o process.o dma.o \
>> traps.o time.o irq.o entry.o ptrace.o signal.o \
>> sys_call_table.o unwinder.o cacheinfo.o
>>
>> diff --git a/arch/openrisc/kernel/insn.c b/arch/openrisc/kernel/insn.c
>> new file mode 100644
>> index 000000000000..2c97eceee6d7
>> --- /dev/null
>> +++ b/arch/openrisc/kernel/insn.c
>> @@ -0,0 +1,74 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * OpenRISC instruction utils
>> + *
>> + * Linux architectural port borrowing liberally from similar works of
>> + * others. All original copyrights apply as per the original source
>> + * declaration.
>> + *
>> + * OpenRISC implementation:
>> + * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
>> + */
>> +
>> +#include <linux/ptrace.h>
>> +#include <asm/insn-def.h>
>> +
>> +void simulate_pc(struct pt_regs *regs, unsigned int jmp)
>> +{
>> + int displacement;
>> + unsigned int rd, op;
>> +
>> + displacement = sign_extend32(((jmp) & 0x7ffff) << 13, 31);
>> + rd = (jmp & 0x3ffffff) >> 21;
>> + op = jmp >> 26;
>> +
>> + switch (op) {
>> + case l_adrp:
>> + regs->gpr[rd] = displacement + (regs->pc & (-8192));
>> + return;
>> + default:
>> + break;
>> + }
>> +}
>
> This function is not used, what will it be used for? Could you mention it in the
> commit message? Also could you document it?
"simulate_pc" is used by KProbes for single-stepping l.adrp. At the moment, it's
not being used anywhere else. Maybe I should move this function to the second commit
which has the KProbe changes.
Also, this function can be removed altogether if we choose to blacklist l.adrp.
>> +
>> +void simulate_branch(struct pt_regs *regs, unsigned int jmp_insn, bool has_delay_slot)
>
> Since you are using this for more than just instruction simulation now, maybe
> its good to document what it does. For example when we use it below we surround
> it with the in_delay_slot check.
I am a little confused here. The core of the function was already wrapped in the
"in_delay_slot" check [2]. I moved it to a new file so instruction emulation could be
used in other places as well(e.g., in KProbes). Functionality wise, the function is
still the same with the added feature that it also supports CPUs with no delay slot.
>> +{
>> + int displacement;
>> + unsigned int rb, op, jmp;
>> +
>> + displacement = sign_extend32(((jmp_insn) & 0x3ffffff) << 2, 27);
>> + rb = (jmp_insn & 0x0000ffff) >> 11;
>> + op = jmp_insn >> 26;
>
> Naming this variable jmp is a bit misleading. Maybe something like
> link_displacement, link_offset or fallthrough?
Sure, I'll rename this.
>> + jmp = has_delay_slot ? 2 * OPENRISC_INSN_SIZE : OPENRISC_INSN_SIZE;
>> +
>> + switch (op) {
>> + case l_j: /* l.j */
>> + regs->pc += displacement;
>> + return;
>> + case l_jal: /* l.jal */
>> + regs->gpr[9] = regs->pc + jmp;
>> + regs->pc += displacement;
>> + return;
>> + case l_bnf: /* l.bnf */
>> + if (regs->sr & SPR_SR_F)
>> + regs->pc += jmp;
>> + else
>> + regs->pc += displacement;
>> + return;
>> + case l_bf: /* l.bf */
>> + if (regs->sr & SPR_SR_F)
>> + regs->pc += displacement;
>> + else
>> + regs->pc += jmp;
>> + return;
>> + case l_jr: /* l.jr */
>> + regs->pc = regs->gpr[rb];
>> + return;
>> + case l_jalr: /* l.jalr */
>> + regs->gpr[9] = regs->pc + jmp;
>> + regs->pc = regs->gpr[rb];
>> + return;
>> + default:
>> + break;
>> + }
>> +}
>> diff --git a/arch/openrisc/kernel/jump_label.c b/arch/openrisc/kernel/jump_label.c
>> index ab7137c23b46..fe082eb847a4 100644
>> --- a/arch/openrisc/kernel/jump_label.c
>> +++ b/arch/openrisc/kernel/jump_label.c
>> @@ -34,7 +34,7 @@ bool arch_jump_label_transform_queue(struct jump_entry *entry,
>>
>> insn = offset;
>> } else {
>> - insn = OPENRISC_INSN_NOP;
>> + insn = INSN_NOP;
>> }
>>
>> if (early_boot_irqs_disabled)
>> diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c
>> index c195be9cc9fc..ee87a3af34fc 100644
>> --- a/arch/openrisc/kernel/traps.c
>> +++ b/arch/openrisc/kernel/traps.c
>> @@ -32,6 +32,7 @@
>>
>> #include <asm/bug.h>
>> #include <asm/fpu.h>
>> +#include <asm/insn-def.h>
>> #include <asm/io.h>
>> #include <asm/processor.h>
>> #include <asm/unwinder.h>
>> @@ -269,47 +270,9 @@ static inline int in_delay_slot(struct pt_regs *regs)
>>
>> static inline void adjust_pc(struct pt_regs *regs, unsigned long address)
>> {
>> - int displacement;
>> - unsigned int rb, op, jmp;
>> -
>> if (unlikely(in_delay_slot(regs))) {
>> /* In delay slot, instruction at pc is a branch, simulate it */
>> - jmp = *((unsigned int *)regs->pc);
>> -
>> - displacement = sign_extend32(((jmp) & 0x3ffffff) << 2, 27);
>> - rb = (jmp & 0x0000ffff) >> 11;
>> - op = jmp >> 26;
>> -
>> - switch (op) {
>> - case 0x00: /* l.j */
>> - regs->pc += displacement;
>> - return;
>> - case 0x01: /* l.jal */
>> - regs->pc += displacement;
>> - regs->gpr[9] = regs->pc + 8;
>> - return;
>> - case 0x03: /* l.bnf */
>> - if (regs->sr & SPR_SR_F)
>> - regs->pc += 8;
>> - else
>> - regs->pc += displacement;
>> - return;
>> - case 0x04: /* l.bf */
>> - if (regs->sr & SPR_SR_F)
>> - regs->pc += displacement;
>> - else
>> - regs->pc += 8;
>> - return;
>> - case 0x11: /* l.jr */
>> - regs->pc = regs->gpr[rb];
>> - return;
>> - case 0x12: /* l.jalr */
>> - regs->pc = regs->gpr[rb];
>> - regs->gpr[9] = regs->pc + 8;
>> - return;
>> - default:
>> - break;
>> - }
>> + simulate_branch(regs, *((unsigned int *)regs->pc), has_delay_slot());
>> } else {
>> regs->pc += 4;
>> }
>> --
>> 2.53.0
>>
Thanks,
Sahil
[1] https://github.com/openrisc/linux/blob/for-next/arch/openrisc/kernel/traps.c#L248
[2] https://github.com/openrisc/linux/blob/for-next/arch/openrisc/kernel/traps.c#L275-L312
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions
2026-04-14 17:11 ` Stafford Horne
2026-04-15 6:10 ` Sahil
@ 2026-04-15 6:39 ` Masami Hiramatsu
2026-04-16 4:57 ` Sahil
1 sibling, 1 reply; 15+ messages in thread
From: Masami Hiramatsu @ 2026-04-15 6:39 UTC (permalink / raw)
To: Stafford Horne
Cc: Sahil Siddiq, jonas, stefan.kristiansson, naveen, davem,
mhiramat, peterz, jpoimboe, jbaron, rostedt, ardb, chenmiao.ku,
johannes, nsc, masahiroy, tytso, linux-openrisc, linux-kernel,
linux-trace-kernel
On Tue, 14 Apr 2026 18:11:37 +0100
Stafford Horne <shorne@gmail.com> wrote:
> Hello Sahil,
>
> Thanks for your patches. I have a few questions. so this is not really a
> technical review at the moment just some main items.
>
> On Wed, Apr 08, 2026 at 12:26:49AM +0530, Sahil Siddiq wrote:
> > Introduce new instruction-related utilities and macros for OpenRISC.
> > This is in preparation for patches that add tracing support such as
> > KProbes.
> >
> > Simulate l.adrp. Fix bugs in simulation of l.jal and l.jalr. Earlier,
>
> Why emulate l.adrp? We don't really use this yet in any OpenRISC code. This
> instruction is meant to be used to help -fpic code, see:
>
> https://openrisc.io/proposals/ladrp
Maybe because l.adrp depends on InsnAddr (instruction address),
if kprobe runs copied instruction on its trampoline buffer, it
generates a wrong result. If it may not be used in the kernel,
arch_prepare_kprobe() can detect it and return an error.
[...]
> > +
> > +#define OPENRISC_INSN_SIZE (sizeof(union openrisc_instruction))
This should be defined within an architecture header file.
> > +
> > +/* Helpers for working with l.trap */
> > +static inline unsigned long __emit_trap(unsigned int code)
> > +{
> > + return (code & 0xffff) | OPCODE_TRAP;
> > +}
> > +
> > +static inline bool has_delay_slot(void)
> > +{
> > + unsigned int cpucfgr = mfspr(SPR_CPUCFGR);
> > +
> > + return !(cpucfgr & SPR_CPUCFGR_ND);
> > +}
>
> This is for handling CPU's that do not have delay slots. We didn't do this
> before, why are you doing it now? Should we mention this in the git commit
> message?
>
> Also, since this is a static configuration for the CPU should we use static keys
> for this?
>
> > +
> > +void simulate_pc(struct pt_regs *regs, unsigned int jmp);
> > +void simulate_branch(struct pt_regs *regs, unsigned int jmp, bool has_delay_slot);
Also, do not use the same name for a function name and a local variable.
Please rename has_delay_slot() to machine_has_delay_slot() or rename
the has_delay_slot parameter to 'delay_slot'.
[...]
> > +#include <linux/ptrace.h>
> > +#include <asm/insn-def.h>
> > +
> > +void simulate_pc(struct pt_regs *regs, unsigned int jmp)
> > +{
> > + int displacement;
> > + unsigned int rd, op;
> > +
> > + displacement = sign_extend32(((jmp) & 0x7ffff) << 13, 31);
> > + rd = (jmp & 0x3ffffff) >> 21;
Please use GENMASK() macro instead of hex value.
(Moreover, define meaningful named macro instead of the magic number.)
> > + op = jmp >> 26;
> > +
> > + switch (op) {
> > + case l_adrp:
l_adrp seems have 2 modes. Is this OK only supporting 32-bit?
32-bit Implementation:
rD[31:0] ← exts(Immediate[18:0] << 13) + (InstAddr & -8192)
64-bit Implementation:
rD[63:0] ← exts(Immediate[20:0] << 13) + (InstAddr & -8192)
> > + regs->gpr[rd] = displacement + (regs->pc & (-8192));
Please use nicely named macro instead of -8192.
Thanks,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC 0/2] openrisc: Add support for KProbes
2026-04-07 18:56 [RFC 0/2] openrisc: Add support for KProbes Sahil Siddiq
2026-04-07 18:56 ` [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
2026-04-07 18:56 ` [RFC 2/2] openrisc: Add KProbes Sahil Siddiq
@ 2026-04-15 6:48 ` Masami Hiramatsu
2026-04-16 5:00 ` Sahil
2 siblings, 1 reply; 15+ messages in thread
From: Masami Hiramatsu @ 2026-04-15 6:48 UTC (permalink / raw)
To: Sahil Siddiq
Cc: jonas, stefan.kristiansson, shorne, naveen, davem, peterz,
jpoimboe, jbaron, rostedt, ardb, chenmiao.ku, johannes, nsc,
masahiroy, tytso, linux-openrisc, linux-kernel,
linux-trace-kernel
Hi Sahil,
On Wed, 8 Apr 2026 00:26:48 +0530
Sahil Siddiq <sahilcdq0@gmail.com> wrote:
> Hi,
>
> This series adds basic support for KProbes on OpenRISC. There are
> a few changes that I would still like to add and test before this
> can be considered for merging. I was hoping to get some feedback on
> the changes made so far. The implementation in this series is based
> on KProbes for LoongArch, MIPS and RISC-V.
Thanks for porting!
Sashiko reviewed this series, can you check the comments?
Most comments (not all) look reasonable to me.
https://sashiko.dev/#/patchset/20260407185650.79816-1-sahilcdq0%40gmail.com
Generally, please make better use of macros rather than magic values
in your code to make it easier to understand.
Also, use GENMASK() and BIT() macro to define bitmasks and bit.
Thanks,
>
> The current state of the series allows traps to be inserted dynamically
> in the kernel. A KProbe can be inserted via a kernel module whose
> init/exit functions are used to register/unregister a KProbe. A pre-
> handler and post-handler can also be provisioned in the module, which
> are associated with the KProbe and triggered when the probe is hit. See
> the documentation on KProbes for a detailed explanation [1].
>
> The following are yet to be implemented for OpenRISC:
> 1. kretprobes
> 2. kprobe-based event tracing
> 3. ftrace, and kprobe features that depend on ftrace (particularly,
> dynamic tracing)
>
> I hope to submit a patch for kretprobes soon (possibly in a revision of
> this series).
>
> I wrote a couple of kernel modules to test these changes. They can be found
> here [2]. I also ran test_kprobes located at ./lib/tests/ against these
> changes [3]. The results are as shown below:
>
> /home # insmod test_kprobes.ko
> KTAP version 1
> 1..1
> KTAP version 1
> # Subtest: kprobes_test
> # module: test_kprobes
> 1..3
> ok 1 test_kprobe
> ok 2 test_kprobes
> ok 3 test_kprobe_missed
> # kprobes_test: pass:3 fail:0 skip:0 total:3
> # Totals: pass:3 fail:0 skip:0 total:3
> ok 1 kprobes_test
> /home #
>
> When compiling the kernel, the following options should be enabled:
> 1. CONFIG_HAVE_KPROBES=y
> 2. CONFIG_KPROBES=y
>
> Also ensure that CONFIG_KPROBE_EVENTS is disabled.
>
> To compile /lib/tests/test_kprobes.c, add the following to .config:
> 1. CONFIG_KUNIT=y
> 2. CONFIG_DEBUG_KERNEL=y
> 3. CONFIG_KPROBES_SANITY_TEST=m
>
> The first commit cleans up and reorganizes existing functions, fixes
> a few issues with instruction simulation, and introduces new structures
> and macros that will be used by KProbes and other tracing facilities
> in the future.
>
> The second commit adds support for KProbes. Currently, I have
> implemented this in such a way that KProbes can't be used to probe
> a few "blacklisted" instructions. Probes can't be inserted in a delay
> slot either (similar to MIPS). I have also added a few asm functions
> to the blacklist that I think should not be probed. For e.g., "memset"
> and "_trap_handler" have been blacklisted because probing them causes
> the kernel to hang. However, I am not sure if other functions in "entry.S"
> need to be added as well to the blacklist.
>
> Thanks,
> Sahil
>
> [1] https://www.kernel.org/doc/html/latest/trace/kprobes.html
> [2] https://github.com/valdaarhun/or-dev/tree/main/home
> [3] https://github.com/openrisc/linux/blob/for-next/lib/tests/test_kprobes.c
>
> Sahil Siddiq (2):
> openrisc: Add utilities and clean up simulation of instructions
> openrisc: Add KProbes
>
> arch/openrisc/Kconfig | 1 +
> arch/openrisc/configs/or1ksim_defconfig | 2 +
> arch/openrisc/configs/virt_defconfig | 2 +
> arch/openrisc/include/asm/asm.h | 22 ++
> arch/openrisc/include/asm/break.h | 19 ++
> arch/openrisc/include/asm/insn-def.h | 61 +++-
> arch/openrisc/include/asm/kprobes.h | 76 +++++
> arch/openrisc/include/asm/spr_defs.h | 1 +
> arch/openrisc/kernel/Makefile | 3 +-
> arch/openrisc/kernel/entry.S | 16 +
> arch/openrisc/kernel/insn.c | 74 +++++
> arch/openrisc/kernel/jump_label.c | 2 +-
> arch/openrisc/kernel/kprobes.c | 381 ++++++++++++++++++++++++
> arch/openrisc/kernel/traps.c | 67 ++---
> arch/openrisc/lib/memcpy.c | 2 +
> arch/openrisc/lib/memset.S | 4 +
> arch/openrisc/mm/fault.c | 5 +
> samples/kprobes/kprobe_example.c | 8 +
> 18 files changed, 701 insertions(+), 45 deletions(-)
> create mode 100644 arch/openrisc/include/asm/asm.h
> create mode 100644 arch/openrisc/include/asm/break.h
> create mode 100644 arch/openrisc/include/asm/kprobes.h
> create mode 100644 arch/openrisc/kernel/insn.c
> create mode 100644 arch/openrisc/kernel/kprobes.c
>
> --
> 2.53.0
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions
2026-04-15 6:39 ` Masami Hiramatsu
@ 2026-04-16 4:57 ` Sahil
0 siblings, 0 replies; 15+ messages in thread
From: Sahil @ 2026-04-16 4:57 UTC (permalink / raw)
To: Masami Hiramatsu (Google), Stafford Horne
Cc: jonas, stefan.kristiansson, naveen, davem, peterz, jpoimboe,
jbaron, rostedt, ardb, chenmiao.ku, johannes, nsc, masahiroy,
tytso, linux-openrisc, linux-kernel, linux-trace-kernel
Hi Masami,
Thank you for your review.
On 4/15/26 12:09 PM, Masami Hiramatsu (Google) wrote:
> On Tue, 14 Apr 2026 18:11:37 +0100
> Stafford Horne <shorne@gmail.com> wrote:
>
>> Hello Sahil,
>>
>> Thanks for your patches. I have a few questions. so this is not really a
>> technical review at the moment just some main items.
>>
>> On Wed, Apr 08, 2026 at 12:26:49AM +0530, Sahil Siddiq wrote:
>>> Introduce new instruction-related utilities and macros for OpenRISC.
>>> This is in preparation for patches that add tracing support such as
>>> KProbes.
>>>
>>> Simulate l.adrp. Fix bugs in simulation of l.jal and l.jalr. Earlier,
>>
>> Why emulate l.adrp? We don't really use this yet in any OpenRISC code. This
>> instruction is meant to be used to help -fpic code, see:
>>
>> https://openrisc.io/proposals/ladrp
>
> Maybe because l.adrp depends on InsnAddr (instruction address),
> if kprobe runs copied instruction on its trampoline buffer, it
> generates a wrong result. If it may not be used in the kernel,
> arch_prepare_kprobe() can detect it and return an error.
>
> [...]
>>> +
>>> +#define OPENRISC_INSN_SIZE (sizeof(union openrisc_instruction))
>
> This should be defined within an architecture header file.
Sorry, I am a little confused here. OPENRISC_INSN_SIZE is in OpenRISC's
instruction definition file (arch/openrisc/include/asm/insn-def.h).
The instruction size for Arm has also been set in it's own insn-def.h
file [1].
>>> +
>>> +/* Helpers for working with l.trap */
>>> +static inline unsigned long __emit_trap(unsigned int code)
>>> +{
>>> + return (code & 0xffff) | OPCODE_TRAP;
>>> +}
>>> +
>>> +static inline bool has_delay_slot(void)
>>> +{
>>> + unsigned int cpucfgr = mfspr(SPR_CPUCFGR);
>>> +
>>> + return !(cpucfgr & SPR_CPUCFGR_ND);
>>> +}
>>
>> This is for handling CPU's that do not have delay slots. We didn't do this
>> before, why are you doing it now? Should we mention this in the git commit
>> message?
>>
>> Also, since this is a static configuration for the CPU should we use static keys
>> for this?
>>
>>> +
>>> +void simulate_pc(struct pt_regs *regs, unsigned int jmp);
>>> +void simulate_branch(struct pt_regs *regs, unsigned int jmp, bool has_delay_slot);
>
> Also, do not use the same name for a function name and a local variable.
> Please rename has_delay_slot() to machine_has_delay_slot() or rename
> the has_delay_slot parameter to 'delay_slot'.
>
> [...]
Oh, good catch. I missed this. I'll change this and retest.
>>> +#include <linux/ptrace.h>
>>> +#include <asm/insn-def.h>
>>> +
>>> +void simulate_pc(struct pt_regs *regs, unsigned int jmp)
>>> +{
>>> + int displacement;
>>> + unsigned int rd, op;
>>> +
>>> + displacement = sign_extend32(((jmp) & 0x7ffff) << 13, 31);
>>> + rd = (jmp & 0x3ffffff) >> 21;
>
> Please use GENMASK() macro instead of hex value.
> (Moreover, define meaningful named macro instead of the magic number.)
Understood, I'll fix this.
>>> + op = jmp >> 26;
>>> +
>>> + switch (op) {
>>> + case l_adrp:
>
> l_adrp seems have 2 modes. Is this OK only supporting 32-bit?
>
> 32-bit Implementation:
> rD[31:0] ← exts(Immediate[18:0] << 13) + (InstAddr & -8192)
> 64-bit Implementation:
> rD[63:0] ← exts(Immediate[20:0] << 13) + (InstAddr & -8192)
I think supporting the 32-bit version should be good enough for the time
being, since OpenRISC's implemention in the kernel is 32-bit. Please see
the following comment from the patch series to support jump labels (published
last year) [2]:
> for us it's more correct to use u32. I always thing unsigned long is
> OK in openrisc as we have no 64-bit implementations. But might as well have it
> correct in case we ever add the 64-bit implementation.
I haven't found any OpenRISC patches in the past year that add the 64-bit
implementation.
>>> + regs->gpr[rd] = displacement + (regs->pc & (-8192));
>
> Please use nicely named macro instead of -8192.
Got it. I'll make this change too.
> Thanks,
Thanks,
Sahil
[1] https://github.com/torvalds/linux/blob/master/arch/arm64/include/asm/insn-def.h#L9
[2] https://lore.kernel.org/openrisc/20250805084114.4125333-2-chenmiao.ku@gmail.com/T/#mb4a939437351b705c30ec7f9e9195d519f9b586f
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [RFC 0/2] openrisc: Add support for KProbes
2026-04-15 6:48 ` [RFC 0/2] openrisc: Add support for KProbes Masami Hiramatsu
@ 2026-04-16 5:00 ` Sahil
0 siblings, 0 replies; 15+ messages in thread
From: Sahil @ 2026-04-16 5:00 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: jonas, stefan.kristiansson, shorne, naveen, davem, peterz,
jpoimboe, jbaron, rostedt, ardb, chenmiao.ku, johannes, nsc,
masahiroy, tytso, linux-openrisc, linux-kernel,
linux-trace-kernel
Hi Masami,
On 4/15/26 12:18 PM, Masami Hiramatsu (Google) wrote:
> Hi Sahil,
>
> On Wed, 8 Apr 2026 00:26:48 +0530
> Sahil Siddiq <sahilcdq0@gmail.com> wrote:
>
>> Hi,
>>
>> This series adds basic support for KProbes on OpenRISC. There are
>> a few changes that I would still like to add and test before this
>> can be considered for merging. I was hoping to get some feedback on
>> the changes made so far. The implementation in this series is based
>> on KProbes for LoongArch, MIPS and RISC-V.
>
> Thanks for porting!
> Sashiko reviewed this series, can you check the comments?
> Most comments (not all) look reasonable to me.
>
> https://sashiko.dev/#/patchset/20260407185650.79816-1-sahilcdq0%40gmail.com
Thank you for the link. I'll address the review comments.
> Generally, please make better use of macros rather than magic values
> in your code to make it easier to understand.
> Also, use GENMASK() and BIT() macro to define bitmasks and bit.
Understood. I'll keep that in mind in future patches.
> Thanks,
>
Thanks,
Sahil
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 0/4] openrisc: Add support for KProbes
2026-04-07 18:56 ` [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
2026-04-14 17:11 ` Stafford Horne
@ 2026-09-30 20:31 ` Sahil Siddiq
2026-09-30 21:18 ` Sahil
2026-09-30 20:31 ` [PATCH v2 1/4] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
` (3 subsequent siblings)
5 siblings, 1 reply; 15+ messages in thread
From: Sahil Siddiq @ 2026-09-30 20:31 UTC (permalink / raw)
To: jonas, stefan.kristiansson, shorne, naveen, davem, mhiramat
Cc: tytso, wangruikang, namcao, pjw, kees, linux-openrisc,
linux-kernel, linux-trace-kernel, Sahil Siddiq
Hi,
Apologies for the delay. This is v2 of the patch series to add KProbe
support for OpenRISC.
The series also adds support for Kretprobes (which depends on the rethook
framework [1]). Kretprobes are fired on function return.
In order to build the kernel with Kprobes and Kretprobes enabled, the
following config options are required:
1. CONFIG_HAVE_KPROBES=y
2. CONFIG_KPROBES=y
3. CONFIG_RETHOOK=y
4. CONFIG_KRETPROBES=y
I tested the generic functionality of Kprobes and Kretprobes using the
tests located at "lib/tests" [2], samples located at "samples/kprobes" [3],
as well as a few test modules that I have published here [4]. The following
options are required to build the samples and tests:
1. CONFIG_SAMPLES=y
2. CONFIG_SAMPLE_KPROBES=m
3. CONFIG_SAMPLE_KRETPROBES=m
4. CONFIG_KPROBES_SANITY_TEST=m
I have also added unit tests at "arch/openrisc/kernel/tests" to verify
that Kprobes work properly when inserted at branch instructions, and
that delay slots are handled correctly. The tests are based on the Kprobe
testsuite added for RISC-V. The following options need to be enabled to
build the tests:
1. CONFIG_KUNIT=y
2. CONFIG_RUNTIME_KERNEL_TESTING_MENU=y
3. CONFIG_OR_KPROBES_KUNIT=m
Below are the results of the tests and sample modules when run in my
environment.
- kretprobe_example:
/home # insmod kretprobe_example.ko
Planted return probe at kernel_clone: (ptrval)
/home # ls >/dev/null
kernel_clone returned 35 and took 1302450 ns to execute
/home # rmmod kretprobe_example.ko
kernel_clone returned 36 and took 1121200 ns to execute
kretprobe at (ptrval) unregistered
Missed probing 0 instances of kernel_clone
- test_kprobes:
/home # insmod test_kprobes.ko
KTAP version 1
1..1
KTAP version 1
# Subtest: kprobes_test
# module: test_kprobes
1..5
ok 1 test_kprobe
ok 2 test_kprobes
ok 3 test_kprobe_missed
ok 4 test_kretprobe
ok 5 test_kretprobes
# kprobes_test: pass:5 fail:0 skip:0 total:5
# Totals: pass:5 fail:0 skip:0 total:5
ok 1 kprobes_test
- openrisc_test_kprobes:
/home # insmod openrisc_test_kprobes.ko
KTAP version 1
1..1
KTAP version 1
# Subtest: kprobes_openrisc
# module: openrisc_test_kprobes
1..1
ok 1 test_kprobe_openrisc
ok 1 kprobes_openrisc
I have also made changes based on the review comments I received for
v1 [5] including several of Sachiko's comments [6]. I chose not to make
changes in a few places. I'll send emails as a reply to this thread
explaining my reasoning inline.
Thanks,
Sahil
[1] https://lkml.rescloud.iu.edu/2203.0/05974.html
[2] https://github.com/torvalds/linux/tree/master/lib/tests
[3] https://github.com/torvalds/linux/tree/master/samples/kprobes
[4] https://github.com/valdaarhun/or-dev
[5] https://lwn.net/ml/all/20260407185650.79816-1-sahilcdq0@gmail.com/
[6] https://sashiko.dev/#/patchset/20260407185650.79816-1-sahilcdq0%40gmail.com
Sahil Siddiq (4):
openrisc: Add utilities and clean up simulation of instructions
openrisc: Add KProbes
openrisc: Add unit tests for KProbes on branch instructions
openrisc: Add Kretprobes
arch/openrisc/Kconfig | 3 +
arch/openrisc/Kconfig.debug | 30 +-
arch/openrisc/configs/or1ksim_defconfig | 2 +
arch/openrisc/configs/virt_defconfig | 2 +
arch/openrisc/include/asm/asm.h | 22 +
arch/openrisc/include/asm/break.h | 19 +
arch/openrisc/include/asm/cpuinfo.h | 3 +
arch/openrisc/include/asm/insn-def.h | 64 ++-
arch/openrisc/include/asm/kprobes.h | 77 ++++
arch/openrisc/include/asm/spr_defs.h | 1 +
arch/openrisc/kernel/Makefile | 5 +-
arch/openrisc/kernel/entry.S | 16 +
arch/openrisc/kernel/insn.c | 84 ++++
arch/openrisc/kernel/kprobes.c | 400 ++++++++++++++++++
arch/openrisc/kernel/rethook.c | 32 ++
arch/openrisc/kernel/rethook.h | 8 +
arch/openrisc/kernel/rethook_trampoline.S | 97 +++++
arch/openrisc/kernel/setup.c | 4 +
arch/openrisc/kernel/tests/Makefile | 3 +
arch/openrisc/kernel/tests/test_kprobes.c | 59 +++
arch/openrisc/kernel/tests/test_kprobes.h | 24 ++
arch/openrisc/kernel/tests/test_kprobes_asm.S | 147 +++++++
arch/openrisc/kernel/traps.c | 82 ++--
arch/openrisc/lib/memcpy.c | 2 +
arch/openrisc/lib/memset.S | 4 +
arch/openrisc/mm/fault.c | 5 +
samples/kprobes/kprobe_example.c | 8 +
27 files changed, 1152 insertions(+), 51 deletions(-)
create mode 100644 arch/openrisc/include/asm/asm.h
create mode 100644 arch/openrisc/include/asm/break.h
create mode 100644 arch/openrisc/include/asm/kprobes.h
create mode 100644 arch/openrisc/kernel/insn.c
create mode 100644 arch/openrisc/kernel/kprobes.c
create mode 100644 arch/openrisc/kernel/rethook.c
create mode 100644 arch/openrisc/kernel/rethook.h
create mode 100644 arch/openrisc/kernel/rethook_trampoline.S
create mode 100644 arch/openrisc/kernel/tests/Makefile
create mode 100644 arch/openrisc/kernel/tests/test_kprobes.c
create mode 100644 arch/openrisc/kernel/tests/test_kprobes.h
create mode 100644 arch/openrisc/kernel/tests/test_kprobes_asm.S
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 1/4] openrisc: Add utilities and clean up simulation of instructions
2026-04-07 18:56 ` [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
2026-04-14 17:11 ` Stafford Horne
2026-09-30 20:31 ` [PATCH v2 0/4] openrisc: Add support for KProbes Sahil Siddiq
@ 2026-09-30 20:31 ` Sahil Siddiq
2026-09-30 20:31 ` [PATCH v2 2/4] openrisc: Add KProbes Sahil Siddiq
` (2 subsequent siblings)
5 siblings, 0 replies; 15+ messages in thread
From: Sahil Siddiq @ 2026-09-30 20:31 UTC (permalink / raw)
To: jonas, stefan.kristiansson, shorne, naveen, davem, mhiramat
Cc: tytso, wangruikang, namcao, pjw, kees, linux-openrisc,
linux-kernel, linux-trace-kernel, Sahil Siddiq
Introduce new instruction-related utilities and macros for OpenRISC.
This is in preparation for patches that add tracing support such as
KProbes and Kretprobes.
Currently, PC is first updated and then saved in the link register
(r9) when simulating l.jal and l.jalr. This can result in a corrupted
page table (bad page map in process). Fix this by saving PC in r9
before updating it.
Move branch simulation to its own function, simulate_branch(), so it
can be reused. It's already being used by do_illegal_instruction().
It'll also be used by KProbes in the following commit.
Introduce simulate_pc() to simulate l.adrp. KProbes will require this
instruction to be simulated as well. simulate_branch() and simulate_pc()
need to be handled differently since branch instructions change the
value of PC while l.adrp depends on PC but doesn't change its value.
Use a static key to check if delay slots are disabled. Delay slots are
a part of the CPU's configuration and won't change at runtime. Since
they are often enabled in an OpenRISC CPU, using a static key will
reduce the overhead of checking this flag in the default case while
processing KProbes inserted at branch instructions and simulating them.
Link: https://raw.githubusercontent.com/openrisc/doc/master/openrisc-arch-1.4-rev0.pdf
Signed-off-by: Sahil Siddiq <sahilcdq0@gmail.com>
---
arch/openrisc/include/asm/cpuinfo.h | 3 +
arch/openrisc/include/asm/insn-def.h | 64 +++++++++++++++++++--
arch/openrisc/include/asm/spr_defs.h | 1 +
arch/openrisc/kernel/Makefile | 2 +-
arch/openrisc/kernel/insn.c | 84 ++++++++++++++++++++++++++++
arch/openrisc/kernel/setup.c | 4 ++
arch/openrisc/kernel/traps.c | 79 +++++++++++---------------
7 files changed, 187 insertions(+), 50 deletions(-)
create mode 100644 arch/openrisc/kernel/insn.c
diff --git a/arch/openrisc/include/asm/cpuinfo.h b/arch/openrisc/include/asm/cpuinfo.h
index 3cfc4cf0b019..d6bb8744eba0 100644
--- a/arch/openrisc/include/asm/cpuinfo.h
+++ b/arch/openrisc/include/asm/cpuinfo.h
@@ -15,6 +15,7 @@
#ifndef __ASM_OPENRISC_CPUINFO_H
#define __ASM_OPENRISC_CPUINFO_H
+#include <linux/jump_label.h>
#include <asm/spr.h>
#include <asm/spr_defs.h>
@@ -34,6 +35,8 @@ struct cpuinfo_or1k {
u16 coreid;
};
+DECLARE_STATIC_KEY_TRUE(delay_slot);
+
extern struct cpuinfo_or1k cpuinfo_or1k[NR_CPUS];
extern void setup_cpuinfo(void);
diff --git a/arch/openrisc/include/asm/insn-def.h b/arch/openrisc/include/asm/insn-def.h
index 1e0c028a5b95..3680c0a94faf 100644
--- a/arch/openrisc/include/asm/insn-def.h
+++ b/arch/openrisc/include/asm/insn-def.h
@@ -3,13 +3,69 @@
* Copyright (C) 2025 Chen Miao
*/
+#include <asm/spr.h>
+#include <asm/spr_defs.h>
+#include <linux/bits.h>
+
#ifndef __ASM_OPENRISC_INSN_DEF_H
#define __ASM_OPENRISC_INSN_DEF_H
-/* or1k instructions are always 32 bits. */
-#define OPENRISC_INSN_SIZE 4
-
/* or1k nop instruction code */
-#define OPENRISC_INSN_NOP 0x15000000U
+#define OPENRISC_INSN_CSYNC 0x23000000U
+#define OPENRISC_INSN_MACRC 0x18010000U
+#define OPENRISC_MASK_MACRC 0xfc01ffffU
+#define OPENRISC_INSN_MSYNC 0x22000000U
+#define OPENRISC_INSN_NOP 0x15000000U
+#define OPENRISC_INSN_PSYNC 0x22800000U
+#define OPENRISC_INSN_SYS 0x20000000U
+#define OPENRISC_INSN_TRAP 0x21000000U
+
+#define OPENRISC_OPCODE_ADRP 0x02
+#define OPENRISC_OPCODE_BF 0x04
+#define OPENRISC_OPCODE_BNF 0x03
+#define OPENRISC_OPCODE_J 0x00
+#define OPENRISC_OPCODE_JAL 0x01
+#define OPENRISC_OPCODE_JALR 0x12
+#define OPENRISC_OPCODE_JR 0x11
+#define OPENRISC_OPCODE_LWA 0x1b
+#define OPENRISC_OPCODE_MFSPR 0x2d
+#define OPENRISC_OPCODE_MTSPR 0x30
+#define OPENRISC_OPCODE_RFE 0x09
+#define OPENRISC_OPCODE_SWA 0x33
+
+#define OPENRISC_ADRP_IMM_MASK GENMASK(18, 0)
+#define OPENRISC_ADRP_OFFSET_MASK GENMASK(31, 13)
+#define OPENRISC_ADRP_IMM_SHIFT 13
+#define OPENRISC_ADRP_REG_MASK GENMASK(25, 0)
+#define OPENRISC_ADRP_REG_SHIFT 21
+#define OPENRISC_JUMP_IMM_MASK GENMASK(25, 0)
+#define OPENRISC_JUMP_IMM_SHIFT 2
+#define OPENRISC_JUMP_REG_MASK GENMASK(15, 11)
+#define OPENRISC_JUMP_REG_SHIFT 11
+
+#define OPENRISC_MASK_SYS GENMASK(31, 16)
+#define OPENRISC_MASK_TRAP GENMASK(31, 16)
+#define OPENRISC_MASK_BCODE GENMASK(15, 0)
+
+#define OPENRISC_OPCODE_MASK 0x3f
+#define OPENRISC_OPCODE_SHIFT 0x1a
+
+struct pt_regs;
+
+#define OPENRISC_INSN_SIZE 4
+
+/* Helpers for working with l.trap */
+static inline unsigned long __emit_trap(unsigned long code)
+{
+ return (code & OPENRISC_MASK_BCODE) | OPENRISC_INSN_TRAP;
+}
+
+static __always_inline unsigned char get_6_bit_opcode(unsigned long insn)
+{
+ return (insn >> OPENRISC_OPCODE_SHIFT) & OPENRISC_OPCODE_MASK;
+}
+
+void simulate_pc(struct pt_regs *regs, unsigned long jmp);
+void simulate_branch(struct pt_regs *regs, unsigned long jmp);
#endif /* __ASM_OPENRISC_INSN_DEF_H */
diff --git a/arch/openrisc/include/asm/spr_defs.h b/arch/openrisc/include/asm/spr_defs.h
index f0b6b492e9f4..5d13a9b96263 100644
--- a/arch/openrisc/include/asm/spr_defs.h
+++ b/arch/openrisc/include/asm/spr_defs.h
@@ -179,6 +179,7 @@
#define SPR_CPUCFGR_OF32S 0x00000080 /* ORFPX32 supported */
#define SPR_CPUCFGR_OF64S 0x00000100 /* ORFPX64 supported */
#define SPR_CPUCFGR_OV64S 0x00000200 /* ORVDX64 supported */
+#define SPR_CPUCFGR_ND 0x00000400 /* No delay slot */
#define SPR_CPUCFGR_RES 0xfffffc00 /* Reserved */
/*
diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile
index 19e0eb94f2eb..150779fbf010 100644
--- a/arch/openrisc/kernel/Makefile
+++ b/arch/openrisc/kernel/Makefile
@@ -5,7 +5,7 @@
always-$(KBUILD_BUILTIN) := vmlinux.lds
-obj-y := head.o setup.o or32_ksyms.o process.o dma.o \
+obj-y := head.o insn.o setup.o or32_ksyms.o process.o dma.o \
traps.o time.o irq.o entry.o ptrace.o signal.o \
sys_call_table.o unwinder.o cacheinfo.o
diff --git a/arch/openrisc/kernel/insn.c b/arch/openrisc/kernel/insn.c
new file mode 100644
index 000000000000..285658512b43
--- /dev/null
+++ b/arch/openrisc/kernel/insn.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * OpenRISC instruction utils
+ *
+ * Linux architectural port borrowing liberally from similar works of
+ * others. All original copyrights apply as per the original source
+ * declaration.
+ *
+ * OpenRISC implementation:
+ * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
+ */
+
+#include <linux/ptrace.h>
+#include <asm/insn-def.h>
+#include <asm/cpuinfo.h>
+
+DEFINE_STATIC_KEY_TRUE(delay_slot);
+
+void simulate_pc(struct pt_regs *regs, unsigned long pc_insn)
+{
+ int page_offset;
+ unsigned int rd, op;
+
+ page_offset = sign_extend32(
+ (pc_insn & OPENRISC_ADRP_IMM_MASK) << OPENRISC_ADRP_IMM_SHIFT, 31);
+ rd = (pc_insn & OPENRISC_ADRP_REG_MASK) >> OPENRISC_ADRP_REG_SHIFT;
+ op = get_6_bit_opcode(pc_insn);
+
+ switch (op) {
+ case OPENRISC_OPCODE_ADRP:
+ regs->gpr[rd] = page_offset + (regs->pc & OPENRISC_ADRP_OFFSET_MASK);
+ regs->pc += OPENRISC_INSN_SIZE;
+ return;
+ default:
+ break;
+ }
+}
+
+void simulate_branch(struct pt_regs *regs, unsigned long jmp_insn)
+{
+ int displacement;
+ unsigned int rb, op, link_offset, temp;
+
+ displacement = sign_extend32(
+ ((jmp_insn) & OPENRISC_JUMP_IMM_MASK) << OPENRISC_JUMP_IMM_SHIFT, 27);
+ rb = (jmp_insn & OPENRISC_JUMP_REG_MASK) >> OPENRISC_JUMP_REG_SHIFT;
+ op = get_6_bit_opcode(jmp_insn);
+ link_offset = OPENRISC_INSN_SIZE;
+
+ if (static_branch_likely(&delay_slot))
+ link_offset <<= 1;
+
+ switch (op) {
+ case OPENRISC_OPCODE_J: /* l.j */
+ regs->pc += displacement;
+ return;
+ case OPENRISC_OPCODE_JAL: /* l.jal */
+ regs->gpr[9] = regs->pc + link_offset;
+ regs->pc += displacement;
+ return;
+ case OPENRISC_OPCODE_BNF: /* l.bnf */
+ if (regs->sr & SPR_SR_F)
+ regs->pc += link_offset;
+ else
+ regs->pc += displacement;
+ return;
+ case OPENRISC_OPCODE_BF: /* l.bf */
+ if (regs->sr & SPR_SR_F)
+ regs->pc += displacement;
+ else
+ regs->pc += link_offset;
+ return;
+ case OPENRISC_OPCODE_JR: /* l.jr */
+ regs->pc = regs->gpr[rb];
+ return;
+ case OPENRISC_OPCODE_JALR: /* l.jalr */
+ temp = regs->pc + link_offset;
+ regs->pc = regs->gpr[rb];
+ regs->gpr[9] = temp;
+ return;
+ default:
+ break;
+ }
+}
diff --git a/arch/openrisc/kernel/setup.c b/arch/openrisc/kernel/setup.c
index 000a9cc10e6f..0f69c36472d5 100644
--- a/arch/openrisc/kernel/setup.c
+++ b/arch/openrisc/kernel/setup.c
@@ -255,6 +255,10 @@ void __init setup_arch(char **cmdline_p)
/* paging_init() sets up the MMU and marks all pages as reserved */
paging_init();
+ /* determine if the processor supports delay slots */
+ if (mfspr(SPR_CPUCFGR) & SPR_CPUCFGR_ND)
+ static_branch_disable(&delay_slot);
+
*cmdline_p = boot_command_line;
printk(KERN_INFO "OpenRISC Linux -- http://openrisc.io\n");
diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c
index c195be9cc9fc..c9ab630ec895 100644
--- a/arch/openrisc/kernel/traps.c
+++ b/arch/openrisc/kernel/traps.c
@@ -32,6 +32,7 @@
#include <asm/bug.h>
#include <asm/fpu.h>
+#include <asm/insn-def.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/unwinder.h>
@@ -215,6 +216,32 @@ asmlinkage void do_trap(struct pt_regs *regs, unsigned long address)
if (user_mode(regs)) {
force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->pc);
} else {
+ unsigned long trap, bcode;
+
+ if (get_kernel_nofault(trap, (unsigned long *)regs->pc) < 0)
+ return;
+
+ /*
+ * Trap instruction was probably removed and no further processing
+ * is required.
+ */
+ if ((trap & OPENRISC_MASK_TRAP) != OPENRISC_INSN_TRAP)
+ return;
+
+ bcode = (trap & OPENRISC_MASK_BCODE);
+ switch (bcode) {
+ case BRK_KPROBE_BP:
+ if (kprobe_breakpoint_handler(regs))
+ return;
+ break;
+ case BRK_KPROBE_SSTEPBP:
+ if (kprobe_singlestep_handler(regs))
+ return;
+ break;
+ default:
+ break;
+ }
+
pr_emerg("KERNEL: Illegal trap exception 0x%.8lx\n", regs->pc);
die("Die:", regs, SIGILL);
}
@@ -252,12 +279,12 @@ static inline int in_delay_slot(struct pt_regs *regs)
insn = *((unsigned int *)regs->pc);
op = insn >> 26;
switch (op) {
- case 0x00: /* l.j */
- case 0x01: /* l.jal */
- case 0x03: /* l.bnf */
- case 0x04: /* l.bf */
- case 0x11: /* l.jr */
- case 0x12: /* l.jalr */
+ case OPENRISC_OPCODE_J: /* l.j */
+ case OPENRISC_OPCODE_JAL: /* l.jal */
+ case OPENRISC_OPCODE_BNF: /* l.bnf */
+ case OPENRISC_OPCODE_BF: /* l.bf */
+ case OPENRISC_OPCODE_JR: /* l.jr */
+ case OPENRISC_OPCODE_JALR: /* l.jalr */
return 1;
default:
return 0;
@@ -269,47 +296,9 @@ static inline int in_delay_slot(struct pt_regs *regs)
static inline void adjust_pc(struct pt_regs *regs, unsigned long address)
{
- int displacement;
- unsigned int rb, op, jmp;
-
if (unlikely(in_delay_slot(regs))) {
/* In delay slot, instruction at pc is a branch, simulate it */
- jmp = *((unsigned int *)regs->pc);
-
- displacement = sign_extend32(((jmp) & 0x3ffffff) << 2, 27);
- rb = (jmp & 0x0000ffff) >> 11;
- op = jmp >> 26;
-
- switch (op) {
- case 0x00: /* l.j */
- regs->pc += displacement;
- return;
- case 0x01: /* l.jal */
- regs->pc += displacement;
- regs->gpr[9] = regs->pc + 8;
- return;
- case 0x03: /* l.bnf */
- if (regs->sr & SPR_SR_F)
- regs->pc += 8;
- else
- regs->pc += displacement;
- return;
- case 0x04: /* l.bf */
- if (regs->sr & SPR_SR_F)
- regs->pc += displacement;
- else
- regs->pc += 8;
- return;
- case 0x11: /* l.jr */
- regs->pc = regs->gpr[rb];
- return;
- case 0x12: /* l.jalr */
- regs->pc = regs->gpr[rb];
- regs->gpr[9] = regs->pc + 8;
- return;
- default:
- break;
- }
+ simulate_branch(regs, *((unsigned int *)regs->pc));
} else {
regs->pc += 4;
}
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 2/4] openrisc: Add KProbes
2026-04-07 18:56 ` [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
` (2 preceding siblings ...)
2026-09-30 20:31 ` [PATCH v2 1/4] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
@ 2026-09-30 20:31 ` Sahil Siddiq
2026-09-30 20:31 ` [PATCH v2 3/4] openrisc: Add unit tests for KProbes on branch instructions Sahil Siddiq
2026-09-30 20:31 ` [PATCH v2 4/4] openrisc: Add Kretprobes Sahil Siddiq
5 siblings, 0 replies; 15+ messages in thread
From: Sahil Siddiq @ 2026-09-30 20:31 UTC (permalink / raw)
To: jonas, stefan.kristiansson, shorne, naveen, davem, mhiramat
Cc: tytso, wangruikang, namcao, pjw, kees, linux-openrisc,
linux-kernel, linux-trace-kernel, Sahil Siddiq
Add KProbes support for OpenRISC. This work is primarily based
on similar work done for LoongArch, MIPS and RISC-V.
KProbes make it possible to trap at almost any address in the
kernel to collect performance/debugging info.
Signed-off-by: Sahil Siddiq <sahilcdq0@gmail.com>
---
arch/openrisc/Kconfig | 1 +
arch/openrisc/configs/or1ksim_defconfig | 2 +
arch/openrisc/configs/virt_defconfig | 2 +
arch/openrisc/include/asm/asm.h | 22 ++
arch/openrisc/include/asm/break.h | 19 ++
arch/openrisc/include/asm/kprobes.h | 77 +++++
arch/openrisc/kernel/Makefile | 1 +
arch/openrisc/kernel/entry.S | 16 +
arch/openrisc/kernel/kprobes.c | 400 ++++++++++++++++++++++++
arch/openrisc/kernel/traps.c | 3 +
arch/openrisc/lib/memcpy.c | 2 +
arch/openrisc/lib/memset.S | 4 +
arch/openrisc/mm/fault.c | 5 +
samples/kprobes/kprobe_example.c | 8 +
14 files changed, 562 insertions(+)
create mode 100644 arch/openrisc/include/asm/asm.h
create mode 100644 arch/openrisc/include/asm/break.h
create mode 100644 arch/openrisc/include/asm/kprobes.h
create mode 100644 arch/openrisc/kernel/kprobes.c
diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig
index 9156635dd264..d240533b424b 100644
--- a/arch/openrisc/Kconfig
+++ b/arch/openrisc/Kconfig
@@ -27,6 +27,7 @@ config OPENRISC
select HAVE_ARCH_JUMP_LABEL
select HAVE_ARCH_JUMP_LABEL_RELATIVE
select HAVE_PCI
+ select HAVE_KPROBES
select HAVE_UID16
select HAVE_PAGE_SIZE_8KB
select HAVE_REGS_AND_STACK_ACCESS_API
diff --git a/arch/openrisc/configs/or1ksim_defconfig b/arch/openrisc/configs/or1ksim_defconfig
index 769705ac24d5..24d2915e7609 100644
--- a/arch/openrisc/configs/or1ksim_defconfig
+++ b/arch/openrisc/configs/or1ksim_defconfig
@@ -10,7 +10,9 @@ CONFIG_EXPERT=y
# CONFIG_KALLSYMS is not set
CONFIG_BUILTIN_DTB_NAME="or1ksim"
CONFIG_HZ_100=y
+CONFIG_OPENRISC=y
CONFIG_JUMP_LABEL=y
+CONFIG_KPROBES=y
CONFIG_MODULES=y
# CONFIG_BLOCK is not set
CONFIG_SLUB_TINY=y
diff --git a/arch/openrisc/configs/virt_defconfig b/arch/openrisc/configs/virt_defconfig
index 0b9979b35ca8..2eccb506032f 100644
--- a/arch/openrisc/configs/virt_defconfig
+++ b/arch/openrisc/configs/virt_defconfig
@@ -11,8 +11,10 @@ CONFIG_OPENRISC_HAVE_INST_SEXT=y
CONFIG_NR_CPUS=8
CONFIG_SMP=y
CONFIG_HZ_100=y
+CONFIG_OPENRISC=y
# CONFIG_OPENRISC_NO_SPR_SR_DSX is not set
CONFIG_JUMP_LABEL=y
+CONFIG_KPROBES=y
# CONFIG_COMPAT_BRK is not set
CONFIG_NET=y
CONFIG_PACKET=y
diff --git a/arch/openrisc/include/asm/asm.h b/arch/openrisc/include/asm/asm.h
new file mode 100644
index 000000000000..aafe08029b4b
--- /dev/null
+++ b/arch/openrisc/include/asm/asm.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Macros for OpenRISC asm
+ *
+ * Linux architectural port borrowing nearly verbatim from
+ * LoongArch and Arm. All original copyrights apply as per
+ * the original source declaration.
+ */
+
+#ifndef __ASM_ASM_H
+#define __ASM_ASM_H
+
+#ifdef CONFIG_KPROBES
+#define _ASM_NOKPROBE(symbol) \
+ .pushsection "_kprobe_blacklist", "aw"; \
+ .long symbol; \
+ .popsection
+#else
+#define _ASM_NOKPROBE(symbol)
+#endif
+
+#endif /* __ASM_ASM_H */
diff --git a/arch/openrisc/include/asm/break.h b/arch/openrisc/include/asm/break.h
new file mode 100644
index 000000000000..04a74d863eef
--- /dev/null
+++ b/arch/openrisc/include/asm/break.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * OpenRISC trap codes used internally by the kernel
+ *
+ * Linux architectural port borrowing liberally from similar works of
+ * others. All original copyrights apply as per the original source
+ * declaration.
+ *
+ * Modifications for the OpenRISC architecture:
+ * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
+ */
+
+#ifndef __ASM_BREAK_H
+#define __ASM_BREAK_H
+
+#define BRK_KPROBE_BP 512 /* Kprobe break */
+#define BRK_KPROBE_SSTEPBP 1024 /* Kprobe single-step software implementation */
+
+#endif /* __ASM_BREAK_H */
diff --git a/arch/openrisc/include/asm/kprobes.h b/arch/openrisc/include/asm/kprobes.h
new file mode 100644
index 000000000000..2b5847002141
--- /dev/null
+++ b/arch/openrisc/include/asm/kprobes.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * OpenRISC Linux
+ *
+ * Linux architectural port borrowing liberally from similar works of
+ * others. All original copyrights apply as per the original source
+ * declaration.
+ *
+ * OpenRISC implementation:
+ * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
+ */
+
+#ifndef __ASM_OPENRISC_KPROBES_H
+#define __ASM_OPENRISC_KPROBES_H
+
+#include <asm-generic/kprobes.h>
+
+#ifdef CONFIG_KPROBES
+#include <asm/break.h>
+#include <asm/cacheflush.h>
+
+#define __ARCH_WANT_KPROBES_INSN_SLOT
+
+struct pt_regs;
+struct kprobe;
+
+typedef u32 kprobe_opcode_t;
+
+/*
+ * MAX_INSN_SIZE is used as the number of slots in an executable
+ * page for single-stepping out of line (SSOL). We need two slots
+ * since we single-step using software breakpoints. The probed
+ * instruction is placed in the first slot with a breakpoint
+ * instruction in the second slot.
+ */
+#define MAX_INSN_SIZE 2
+
+/* Architecture specific copy of original instruction */
+struct arch_specific_insn {
+ /* copy of original instruction */
+ kprobe_opcode_t *insn;
+};
+
+struct prev_kprobe {
+ struct kprobe *kp;
+ unsigned int status;
+ unsigned long restore_pc;
+};
+
+/* per-cpu kprobe control block */
+struct kprobe_ctlblk {
+ unsigned int kprobe_status;
+ unsigned long irq_flags;
+ /* address of next instruction in case of SSOL */
+ unsigned long restore_pc;
+ struct prev_kprobe prev_kprobe;
+};
+
+#define flush_insn_slot(p) do { } while (0)
+#define kretprobe_blacklist_size 0
+
+void arch_remove_kprobe(struct kprobe *p);
+int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
+bool kprobe_breakpoint_handler(struct pt_regs *regs);
+bool kprobe_singlestep_handler(struct pt_regs *regs);
+#else /* !CONFIG_KPROBES */
+static inline bool kprobe_breakpoint_handler(struct pt_regs *regs)
+{
+ return false;
+}
+
+static inline bool kprobe_singlestep_handler(struct pt_regs *regs)
+{
+ return false;
+}
+#endif /* CONFIG_KPROBES */
+#endif /* __ASM_OPENRISC_KPROBES_H */
diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile
index 150779fbf010..2ac824867963 100644
--- a/arch/openrisc/kernel/Makefile
+++ b/arch/openrisc/kernel/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SMP) += smp.o sync-timer.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_OF) += prom.o
+obj-$(CONFIG_KPROBES) += kprobes.o
obj-y += patching.o
clean:
diff --git a/arch/openrisc/kernel/entry.S b/arch/openrisc/kernel/entry.S
index c7e90b09645e..cd28bf1f7a3b 100644
--- a/arch/openrisc/kernel/entry.S
+++ b/arch/openrisc/kernel/entry.S
@@ -15,6 +15,7 @@
#include <linux/linkage.h>
#include <linux/pgtable.h>
+#include <asm/asm.h>
#include <asm/processor.h>
#include <asm/unistd.h>
#include <asm/thread_info.h>
@@ -640,6 +641,7 @@ ENTRY(_sys_call_handler)
/* r30 is the only register we clobber in the fast path */
/* r30 already saved */
/* l.sw PT_GPR30(r1),r30 */
+_ASM_NOKPROBE(_sys_call_handler)
_syscall_check_trace_enter:
/* syscalls run with interrupts enabled */
@@ -652,12 +654,14 @@ _syscall_check_trace_enter:
l.sfne r30,r0
l.bf _syscall_trace_enter
l.nop
+_ASM_NOKPROBE(_syscall_check_trace_enter)
_syscall_check:
/* Ensure that the syscall number is reasonable */
l.sfgeui r11,__NR_syscalls
l.bf _syscall_badsys
l.nop
+_ASM_NOKPROBE(_syscall_check)
_syscall_call:
l.movhi r29,hi(sys_call_table)
@@ -668,12 +672,14 @@ _syscall_call:
l.jalr r29
l.nop
+_ASM_NOKPROBE(_syscall_call)
_syscall_return:
/* All syscalls return here... just pay attention to ret_from_fork
* which does it in a round-about way.
*/
l.sw PT_GPR11(r1),r11 // save return value
+_ASM_NOKPROBE(_syscall_return)
#if 0
_syscall_debug:
@@ -708,6 +714,7 @@ _syscall_check_trace_leave:
l.sfne r30,r0
l.bf _syscall_trace_leave
l.nop
+_ASM_NOKPROBE(_syscall_check_trace_leave)
/* This is where the exception-return code begins... interrupts need to be
* disabled the rest of the way here because we can't afford to miss any
@@ -744,6 +751,7 @@ _syscall_check_work:
/* _work_pending needs to be called with interrupts disabled */
l.j _work_pending
l.nop
+_ASM_NOKPROBE(_syscall_check_work)
_syscall_resume_userspace:
// ENABLE_INTERRUPTS(r29)
@@ -800,6 +808,7 @@ _syscall_resume_userspace:
l.mtspr r0,r13,SPR_EPCR_BASE
l.mtspr r0,r15,SPR_ESR_BASE
l.rfe
+_ASM_NOKPROBE(_syscall_resume_userspace)
/* End of hot path!
* Keep the below tracing and error handling out of the hot path...
@@ -829,6 +838,7 @@ _syscall_trace_enter:
l.j _syscall_check
l.lwz r8,PT_GPR8(r1)
+_ASM_NOKPROBE(_syscall_trace_enter)
_syscall_trace_leave:
l.jal do_syscall_trace_leave
@@ -836,6 +846,7 @@ _syscall_trace_leave:
l.j _syscall_check_work
l.nop
+_ASM_NOKPROBE(_syscall_trace_leave)
_syscall_badsys:
/* Here we effectively pretend to have executed an imaginary
@@ -845,6 +856,7 @@ _syscall_badsys:
*/
l.j _syscall_return
l.addi r11,r0,-ENOSYS
+_ASM_NOKPROBE(_syscall_badsys)
/******* END SYSCALL HANDLING *******/
@@ -870,6 +882,7 @@ EXCEPTION_ENTRY(_trap_handler)
l.j _ret_from_exception
l.nop
+_ASM_NOKPROBE(_trap_handler)
/* ---[ 0xf00: Reserved exception ]-------------------------------------- */
@@ -1004,6 +1017,8 @@ ENTRY(_ret_from_exception)
l.nop
l.j _resume_userspace
l.nop
+_ASM_NOKPROBE(_ret_from_exception)
+_ASM_NOKPROBE(_ret_from_intr)
ENTRY(ret_from_fork)
l.jal schedule_tail
@@ -1038,6 +1053,7 @@ ENTRY(ret_from_fork)
l.j _syscall_return
l.nop
+_ASM_NOKPROBE(ret_from_fork)
/* ========================================================[ switch ] === */
diff --git a/arch/openrisc/kernel/kprobes.c b/arch/openrisc/kernel/kprobes.c
new file mode 100644
index 000000000000..3268ffb0d707
--- /dev/null
+++ b/arch/openrisc/kernel/kprobes.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Kernel probes (KProbes) for OpenRISC
+ *
+ * Linux architectural port borrowing liberally from similar works of
+ * others. All original copyrights apply as per the original source
+ * declaration.
+ *
+ * OpenRISC implementation:
+ * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
+ */
+
+#include <linux/kprobes.h>
+#include <asm/cpuinfo.h>
+#include <asm/insn-def.h>
+#include <asm/text-patching.h>
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe);
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+#define KPROBE_BP_INSN __emit_trap(BRK_KPROBE_BP)
+#define KPROBE_SSTEPBP_INSN __emit_trap(BRK_KPROBE_SSTEPBP)
+
+static bool insn_not_supported(kprobe_opcode_t insn)
+{
+ switch (insn) {
+ case OPENRISC_INSN_CSYNC:
+ case OPENRISC_INSN_MSYNC:
+ case OPENRISC_INSN_PSYNC:
+ return true;
+ }
+
+ if ((insn & OPENRISC_MASK_TRAP) == OPENRISC_INSN_TRAP)
+ return true;
+
+ if ((insn & OPENRISC_MASK_SYS) == OPENRISC_INSN_SYS)
+ return true;
+
+ if ((insn & OPENRISC_MASK_MACRC) == OPENRISC_INSN_MACRC)
+ return true;
+
+ unsigned char opcode = get_6_bit_opcode(insn);
+
+ switch (opcode) {
+ case OPENRISC_OPCODE_RFE:
+ case OPENRISC_OPCODE_LWA:
+ case OPENRISC_OPCODE_MFSPR:
+ case OPENRISC_OPCODE_MTSPR:
+ case OPENRISC_OPCODE_SWA:
+ return true;
+ }
+
+ return false;
+}
+NOKPROBE_SYMBOL(insn_not_supported)
+
+static bool is_branch_insn(kprobe_opcode_t insn)
+{
+ unsigned char opcode = get_6_bit_opcode(insn);
+
+ switch (opcode) {
+ case OPENRISC_OPCODE_BF:
+ case OPENRISC_OPCODE_BNF:
+ case OPENRISC_OPCODE_J:
+ case OPENRISC_OPCODE_JAL:
+ case OPENRISC_OPCODE_JALR:
+ case OPENRISC_OPCODE_JR:
+ return true;
+ }
+
+ return false;
+}
+NOKPROBE_SYMBOL(is_branch_insn)
+
+static bool is_pc_insn(kprobe_opcode_t insn)
+{
+ unsigned char opcode = get_6_bit_opcode(insn);
+
+ if (opcode == OPENRISC_OPCODE_ADRP)
+ return true;
+
+ return false;
+}
+NOKPROBE_SYMBOL(is_pc_insn)
+
+static bool insns_need_simulation(kprobe_opcode_t insn, bool *exec_delay_slot)
+{
+ *exec_delay_slot = false;
+
+ if (is_branch_insn(insn)) {
+ if (static_branch_likely(&delay_slot))
+ *exec_delay_slot = true;
+ return true;
+ }
+
+ if (is_pc_insn(insn))
+ return true;
+
+ return false;
+}
+NOKPROBE_SYMBOL(insns_need_simulation)
+
+int arch_prepare_kprobe(struct kprobe *p)
+{
+ kprobe_opcode_t insn;
+ kprobe_opcode_t prev_insn, next_insn;
+ bool exec_delay_slot = false;
+
+ /* Attempt to probe at unaligned address */
+ if ((unsigned long)p->addr & 0x3)
+ return -EILSEQ;
+
+ p->opcode = *p->addr;
+ insn = p->opcode;
+
+ if (insn_not_supported(insn)) {
+ pr_notice("Can't insert KProbe at blacklisted instruction.\n");
+ return -EINVAL;
+ }
+
+ if (copy_from_kernel_nofault(&prev_insn, p->addr - 1,
+ OPENRISC_INSN_SIZE) == 0 && is_branch_insn(prev_insn)) {
+ if (static_branch_likely(&delay_slot)) {
+ pr_notice("Can't insert KProbe in delay slot.\n");
+ return -EINVAL;
+ }
+ }
+
+ if (copy_from_kernel_nofault(&next_insn, p->addr + 1, OPENRISC_INSN_SIZE) == 0 &&
+ insns_need_simulation(next_insn, &exec_delay_slot) && is_branch_insn(insn)) {
+ if (static_branch_likely(&delay_slot)) {
+ pr_notice("Can't insert KProbe if following instruction is in a delay slot and depends on PC.\n");
+ return -EINVAL;
+ }
+ }
+
+ if (insns_need_simulation(insn, &exec_delay_slot) && !exec_delay_slot) {
+ p->ainsn.insn = NULL;
+ } else {
+ /*
+ * Single step probed instruction or, in case of branch instructions, single
+ * step instruction in delay slot.
+ */
+ p->ainsn.insn = get_insn_slot();
+ if (!p->ainsn.insn)
+ return -ENOMEM;
+
+ if (exec_delay_slot)
+ patch_insn_write(p->ainsn.insn, *(p->addr + 1));
+ else
+ patch_insn_write(p->ainsn.insn, p->opcode);
+ patch_insn_write(&p->ainsn.insn[1], KPROBE_SSTEPBP_INSN);
+ }
+
+ return 0;
+}
+NOKPROBE_SYMBOL(arch_prepare_kprobe);
+
+void arch_arm_kprobe(struct kprobe *p)
+{
+ patch_insn_write(p->addr, KPROBE_BP_INSN);
+ flush_insn_slot(p);
+}
+NOKPROBE_SYMBOL(arch_arm_kprobe);
+
+void arch_disarm_kprobe(struct kprobe *p)
+{
+ patch_insn_write(p->addr, p->opcode);
+ flush_insn_slot(p);
+}
+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 set_current_kprobe(struct kprobe *p)
+{
+ __this_cpu_write(current_kprobe, p);
+}
+NOKPROBE_SYMBOL(set_current_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.restore_pc = kcb->restore_pc;
+}
+NOKPROBE_SYMBOL(save_previous_kprobe);
+
+static void restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+ kcb->kprobe_status = kcb->prev_kprobe.status;
+ kcb->restore_pc = kcb->prev_kprobe.restore_pc;
+ set_current_kprobe(kcb->prev_kprobe.kp);
+}
+NOKPROBE_SYMBOL(restore_previous_kprobe);
+
+static void post_kprobe_handler(struct kprobe *cur, struct kprobe_ctlblk *kcb,
+ struct pt_regs *regs)
+{
+ instruction_pointer_set(regs, kcb->restore_pc);
+
+ if (kcb->kprobe_status == KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ return;
+ }
+
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+
+ if (cur->post_handler)
+ cur->post_handler(cur, regs, 0);
+
+ reset_current_kprobe();
+ preempt_enable_no_resched();
+}
+NOKPROBE_SYMBOL(post_kprobe_handler);
+
+static void setup_singlestep(struct kprobe *p, struct pt_regs *regs,
+ struct kprobe_ctlblk *kcb, int reenter)
+{
+ kprobe_opcode_t insn;
+
+ if (reenter) {
+ save_previous_kprobe(kcb);
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_REENTER;
+ } else {
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ }
+
+ /* Emulate instruction if required. */
+ insn = p->opcode;
+ if (is_branch_insn(insn)) {
+ simulate_branch(regs, insn);
+ /* Save target addr before updating PC to SSOL slot */
+ kcb->restore_pc = regs->pc;
+ } else if (is_pc_insn(insn)) {
+ simulate_pc(regs, insn);
+ kcb->restore_pc = regs->pc;
+ /* No SSOL is required here, so call post-process immediately. */
+ post_kprobe_handler(p, kcb, regs);
+ return;
+ } else
+ kcb->restore_pc = regs->pc + OPENRISC_INSN_SIZE;
+
+ if (p->ainsn.insn) {
+ /* Disable IRQs before single stepping */
+ local_irq_save(kcb->irq_flags);
+ instruction_pointer_set(regs, (unsigned long)p->ainsn.insn);
+ } else {
+ /*
+ * The instruction is a branch but delay slots are disabled.
+ * Simply call the post-handler.
+ */
+ post_kprobe_handler(p, kcb, regs);
+ }
+}
+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_SS:
+ case KPROBE_HIT_SSDONE:
+ case KPROBE_HIT_ACTIVE:
+ kprobes_inc_nmissed_count(p);
+ setup_singlestep(p, regs, kcb, 1);
+ break;
+ case KPROBE_REENTER:
+ pr_warn("Unrecoverable KProbe detected.\n");
+ dump_kprobe(p);
+ WARN_ON_ONCE(1);
+ return false;
+ default:
+ WARN_ON(1);
+ return false;
+ }
+
+ return true;
+}
+NOKPROBE_SYMBOL(reenter_kprobe);
+
+bool kprobe_breakpoint_handler(struct pt_regs *regs)
+{
+ struct kprobe *p, *curr_kprobe;
+ struct kprobe_ctlblk *kcb;
+ kprobe_opcode_t *addr = (kprobe_opcode_t *)regs->pc;
+ kprobe_opcode_t insn;
+
+ /*
+ * We don't want to be preempted for the entire
+ * duration of kprobe processing.
+ */
+ preempt_disable();
+
+ kcb = get_kprobe_ctlblk();
+ curr_kprobe = kprobe_running();
+ p = get_kprobe(addr);
+ if (p) {
+ if (curr_kprobe) {
+ if (reenter_kprobe(p, regs, kcb))
+ return true;
+ } else {
+ /* Probe hit */
+ set_current_kprobe(p);
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+ /*
+ * If there's no pre-handler or it returns 0, continue
+ * processing the KProbe as usual. A non-zero return
+ * value implies the saved registers have been modified
+ * and the execution path might deviate from what's
+ * expected. Reset the KProbe and return.
+ */
+ if (!p->pre_handler || !p->pre_handler(p, regs)) {
+ setup_singlestep(p, regs, kcb, 0);
+ } else {
+ reset_current_kprobe();
+ preempt_enable_no_resched();
+ }
+ return true;
+ }
+ }
+
+ /*
+ * The breakpoint instruction was removed right after we hit it
+ * possibly by another cpu. If the original instruction was also
+ * a trap instruction then return to the trap handler for further
+ * processing, else no further processing is required.
+ */
+ if (get_kernel_nofault(insn, addr) < 0
+ || (*addr & OPENRISC_MASK_TRAP) != OPENRISC_INSN_TRAP) {
+ preempt_enable_no_resched();
+ return true;
+ }
+
+ preempt_enable_no_resched();
+ return false;
+}
+NOKPROBE_SYMBOL(kprobe_breakpoint_handler);
+
+bool kprobe_singlestep_handler(struct pt_regs *regs)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ unsigned long addr = instruction_pointer(regs);
+
+ if (cur && (kcb->kprobe_status & (KPROBE_HIT_SS | KPROBE_REENTER)) &&
+ ((unsigned long)&cur->ainsn.insn[1] == addr)) {
+ /* Single stepping has been completed. Enable IRQs. */
+ regs->sr |= kcb->irq_flags;
+ local_irq_restore(kcb->irq_flags);
+ post_kprobe_handler(cur, kcb, regs);
+ return true;
+ }
+
+ return false;
+}
+NOKPROBE_SYMBOL(kprobe_singlestep_handler);
+
+int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+ struct kprobe *cur = kprobe_running();
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ /*
+ * The instruction being single-stepped caused a page fault.
+ * Reset the current KProbe and set PC to the probe's address.
+ * Then allow the page fault handler to continue as usual.
+ */
+ instruction_pointer_set(regs, (unsigned long)cur->addr);
+
+ if (kcb->kprobe_status == KPROBE_REENTER) {
+ restore_previous_kprobe(kcb);
+ } else {
+ regs->sr |= kcb->irq_flags;
+ local_irq_restore(kcb->irq_flags);
+ reset_current_kprobe();
+ preempt_enable_no_resched();
+ }
+ break;
+ }
+
+ return 0;
+}
+NOKPROBE_SYMBOL(kprobe_fault_handler);
+
+int __init arch_init_kprobes(void)
+{
+ return 0;
+}
diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c
index c9ab630ec895..2d6b29ea8c4f 100644
--- a/arch/openrisc/kernel/traps.c
+++ b/arch/openrisc/kernel/traps.c
@@ -30,10 +30,12 @@
#include <linux/kallsyms.h>
#include <linux/uaccess.h>
+#include <asm/break.h>
#include <asm/bug.h>
#include <asm/fpu.h>
#include <asm/insn-def.h>
#include <asm/io.h>
+#include <asm/kprobes.h>
#include <asm/processor.h>
#include <asm/unwinder.h>
#include <asm/sections.h>
@@ -246,6 +248,7 @@ asmlinkage void do_trap(struct pt_regs *regs, unsigned long address)
die("Die:", regs, SIGILL);
}
}
+NOKPROBE_SYMBOL(do_trap)
asmlinkage void do_unaligned_access(struct pt_regs *regs, unsigned long address)
{
diff --git a/arch/openrisc/lib/memcpy.c b/arch/openrisc/lib/memcpy.c
index e2af9b510804..701262a40134 100644
--- a/arch/openrisc/lib/memcpy.c
+++ b/arch/openrisc/lib/memcpy.c
@@ -16,6 +16,7 @@
#include <linux/export.h>
+#include <linux/kprobes.h>
#include <linux/string.h>
#ifdef CONFIG_OR1K_1200
@@ -122,4 +123,5 @@ void *memcpy(void *dest, __const void *src, __kernel_size_t n)
}
#endif
+NOKPROBE_SYMBOL(memcpy);
EXPORT_SYMBOL(memcpy);
diff --git a/arch/openrisc/lib/memset.S b/arch/openrisc/lib/memset.S
index c3ac2a8b68d3..14e63af12d73 100644
--- a/arch/openrisc/lib/memset.S
+++ b/arch/openrisc/lib/memset.S
@@ -9,6 +9,8 @@
* Copyright (C) 2015 Olof Kindgren <olof.kindgren@gmail.com>
*/
+#include <asm/asm.h>
+
.global memset
.type memset, @function
memset:
@@ -92,3 +94,5 @@ memset:
4: l.jr r9
l.ori r11, r3, 0
+
+_ASM_NOKPROBE(memset)
diff --git a/arch/openrisc/mm/fault.c b/arch/openrisc/mm/fault.c
index 29e232d78d82..5263a832562f 100644
--- a/arch/openrisc/mm/fault.c
+++ b/arch/openrisc/mm/fault.c
@@ -14,6 +14,7 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/extable.h>
+#include <linux/kprobes.h>
#include <linux/sched/signal.h>
#include <linux/perf_event.h>
@@ -55,6 +56,9 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address,
tsk = current;
+ if (kprobe_page_fault(regs, vector))
+ return;
+
/*
* We fault-in kernel-space virtual memory on-demand. The
* 'reference' page table is init_mm.pgd.
@@ -351,3 +355,4 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address,
return;
}
}
+NOKPROBE_SYMBOL(do_page_fault)
diff --git a/samples/kprobes/kprobe_example.c b/samples/kprobes/kprobe_example.c
index 53ec6c8b8c40..84e26ebef70b 100644
--- a/samples/kprobes/kprobe_example.c
+++ b/samples/kprobes/kprobe_example.c
@@ -59,6 +59,10 @@ static int __kprobes handler_pre(struct kprobe *p, struct pt_regs *regs)
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
+#ifdef CONFIG_OPENRISC
+ pr_info("<%s> p->addr = 0x%p, pc = 0x%lx, status = 0x%lx\n",
+ p->symbol_name, p->addr, regs->pc, regs->sr);
+#endif
/* A dump_stack() here will give a stack backtrace */
return 0;
@@ -100,6 +104,10 @@ static void __kprobes handler_post(struct kprobe *p, struct pt_regs *regs,
pr_info("<%s> p->addr = 0x%p, estat = 0x%lx\n",
p->symbol_name, p->addr, regs->csr_estat);
#endif
+#ifdef CONFIG_OPENRISC
+ pr_info("<%s> p->addr = 0x%p, status = 0x%lx\n",
+ p->symbol_name, p->addr, regs->sr);
+#endif
}
static int __init kprobe_init(void)
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 3/4] openrisc: Add unit tests for KProbes on branch instructions
2026-04-07 18:56 ` [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
` (3 preceding siblings ...)
2026-09-30 20:31 ` [PATCH v2 2/4] openrisc: Add KProbes Sahil Siddiq
@ 2026-09-30 20:31 ` Sahil Siddiq
2026-09-30 20:31 ` [PATCH v2 4/4] openrisc: Add Kretprobes Sahil Siddiq
5 siblings, 0 replies; 15+ messages in thread
From: Sahil Siddiq @ 2026-09-30 20:31 UTC (permalink / raw)
To: jonas, stefan.kristiansson, shorne, naveen, davem, mhiramat
Cc: tytso, wangruikang, namcao, pjw, kees, linux-openrisc,
linux-kernel, linux-trace-kernel, Sahil Siddiq
Tests already exist lib/tests to verify the generic functionality of
KProbes. Add unit tests to verify that KProbes work on instructions
that require simulation in the OpenRISC ISA, and that the corresponding
delay slots are handled correctly.
Signed-off-by: Sahil Siddiq <sahilcdq0@gmail.com>
---
arch/openrisc/Kconfig.debug | 30 +++-
arch/openrisc/kernel/Makefile | 1 +
arch/openrisc/kernel/tests/Makefile | 3 +
arch/openrisc/kernel/tests/test_kprobes.c | 59 +++++++
arch/openrisc/kernel/tests/test_kprobes.h | 24 +++
arch/openrisc/kernel/tests/test_kprobes_asm.S | 147 ++++++++++++++++++
6 files changed, 263 insertions(+), 1 deletion(-)
create mode 100644 arch/openrisc/kernel/tests/Makefile
create mode 100644 arch/openrisc/kernel/tests/test_kprobes.c
create mode 100644 arch/openrisc/kernel/tests/test_kprobes.h
create mode 100644 arch/openrisc/kernel/tests/test_kprobes_asm.S
diff --git a/arch/openrisc/Kconfig.debug b/arch/openrisc/Kconfig.debug
index 295942fe3fd5..54110f3a111f 100644
--- a/arch/openrisc/Kconfig.debug
+++ b/arch/openrisc/Kconfig.debug
@@ -1,2 +1,30 @@
# SPDX-License-Identifier: GPL-2.0-only
-# dummy file, do not delete
+menu "openrisc Testing"
+
+menuconfig RUNTIME_KERNEL_TESTING_MENU
+ bool "openrisc kernel runtime testing"
+ default y
+ help
+ Enable openrisc kernel runtime testing. With this option, one can
+ build and run tests to verify the correctness of relevant components
+ in the kernel at runtime.
+
+if RUNTIME_KERNEL_TESTING_MENU
+
+config OR_KPROBES_KUNIT
+ tristate "KUnit test for kprobes on openrisc"
+ depends on KUNIT
+ depends on KPROBES
+ default KUNIT_ALL_TESTS
+ help
+ Enable testing of KProbes in OpenRISC for instructions that
+ require simulation. When further changes related to probes or
+ tracing are added to OpenRISC's port, these tests can help
+ verify that instruction simulation and processing of delay slots
+ continue to work as expected.
+
+ If unsure, say N
+
+endif # RUNTIME_KERNEL_TESTING_MENU
+
+endmenu # "openrisc Testing"
diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile
index 2ac824867963..0c1cade09e0c 100644
--- a/arch/openrisc/kernel/Makefile
+++ b/arch/openrisc/kernel/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_OF) += prom.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_OR_KPROBES_KUNIT) += tests/
obj-y += patching.o
clean:
diff --git a/arch/openrisc/kernel/tests/Makefile b/arch/openrisc/kernel/tests/Makefile
new file mode 100644
index 000000000000..9e9693fd3550
--- /dev/null
+++ b/arch/openrisc/kernel/tests/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_OR_KPROBES_KUNIT) += openrisc_test_kprobes.o
+
+openrisc_test_kprobes-objs := test_kprobes.o test_kprobes_asm.o
diff --git a/arch/openrisc/kernel/tests/test_kprobes.c b/arch/openrisc/kernel/tests/test_kprobes.c
new file mode 100644
index 000000000000..a41bdec662e1
--- /dev/null
+++ b/arch/openrisc/kernel/tests/test_kprobes.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KProbe unit tests for OpenRISC
+ *
+ * Unit tests borrowed liberally from similar work of RISC-V. All
+ * original copyrights apply as per the original source declaration.
+ */
+
+// #include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <kunit/test.h>
+#include "test_kprobes.h"
+
+static void test_kprobe_openrisc(struct kunit *test)
+{
+ unsigned int num_kprobe = 0;
+ unsigned long (*func)(void);
+ struct kprobe *kp;
+ int i;
+
+ while (test_kprobes_addresses[num_kprobe])
+ num_kprobe++;
+
+ kp = kcalloc(num_kprobe, sizeof(struct kprobe), GFP_KERNEL);
+ KUNIT_EXPECT_TRUE(test, kp);
+ if (!kp)
+ return;
+
+ for (i = 0; i < num_kprobe; ++i) {
+ kp[i].addr = test_kprobes_addresses[i];
+ KUNIT_EXPECT_EQ(test, 0, register_kprobe(&kp[i]));
+ }
+
+ for (i = 0;; ++i) {
+ func = test_kprobes_functions[i];
+ if (!func)
+ break;
+ KUNIT_EXPECT_EQ_MSG(test, KPROBE_TEST_MAGIC, func(), "function %d broken", i);
+ }
+
+ for (i = 0; i < num_kprobe; ++i)
+ unregister_kprobe(&kp[i]);
+ kfree(kp);
+}
+
+static struct kunit_case kprobes_testcases[] = {
+ KUNIT_CASE(test_kprobe_openrisc),
+ {}
+};
+
+static struct kunit_suite kprobes_test_suite = {
+ .name = "kprobes_openrisc",
+ .test_cases = kprobes_testcases,
+};
+
+kunit_test_suites(&kprobes_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit test for OpenRISC KProbes");
diff --git a/arch/openrisc/kernel/tests/test_kprobes.h b/arch/openrisc/kernel/tests/test_kprobes.h
new file mode 100644
index 000000000000..26c26920d430
--- /dev/null
+++ b/arch/openrisc/kernel/tests/test_kprobes.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Unit tests borrowed liberally from similar work of RISC-V. All
+ * original copyrights apply as per the original source declaration.
+ */
+
+#ifndef TEST_KPROBES_H
+#define TEST_KPROBES_H
+
+#define KPROBE_TEST_MAGIC 0xDEADC0DE
+#define KPROBE_TEST_MAGIC_LOWER 0x0000C0DE
+#define KPROBE_TEST_MAGIC_UPPER 0x0000DEAD
+
+#ifndef __ASSEMBLER__
+
+/* array of addresses to install kprobes */
+extern void *test_kprobes_addresses[];
+
+/* array of functions that return KPROBE_TEST_MAGIC */
+extern unsigned long (*test_kprobes_functions[])(void);
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* TEST_KPROBES_H */
diff --git a/arch/openrisc/kernel/tests/test_kprobes_asm.S b/arch/openrisc/kernel/tests/test_kprobes_asm.S
new file mode 100644
index 000000000000..a9fcf99b55ae
--- /dev/null
+++ b/arch/openrisc/kernel/tests/test_kprobes_asm.S
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * KProbes unit tests for OpenRISC
+ *
+ * Unit tests borrowed liberally from similar work of RISC-V. All
+ * original copyrights apply as per the original source declaration.
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include "test_kprobes.h"
+
+SYM_FUNC_START(test_kprobes_j)
+test_kprobes_j_addr1:
+ l.j 2f
+ l.nop
+1: l.ori r13,r13,KPROBE_TEST_MAGIC_LOWER
+ l.addi r11,r13,0
+ l.jr r9
+ l.nop
+2: l.movhi r13,KPROBE_TEST_MAGIC_UPPER
+test_kprobes_j_addr2:
+ l.j 1b
+ l.nop
+SYM_FUNC_END(test_kprobes_j)
+
+SYM_FUNC_START(test_kprobes_jal)
+ l.addi r15,r9,0
+test_kprobes_jal_addr1:
+ l.jal 2f
+ l.nop
+1: l.movhi r13,KPROBE_TEST_MAGIC_UPPER
+ l.jr r9
+ l.nop
+test_kprobes_jal_addr2:
+2: l.jal 1b
+ l.nop
+ l.ori r13,r13,KPROBE_TEST_MAGIC_LOWER
+ l.addi r11,r13,0
+ l.addi r9,r15,0
+ l.jr r9
+ l.nop
+SYM_FUNC_END(test_kprobes_jal)
+
+SYM_FUNC_START(test_kprobes_jr)
+ l.movhi r15,hi(2f)
+ l.ori r15,r15,lo(2f)
+test_kprobes_jr_addr1:
+ l.jr r15
+ l.nop
+1: l.ori r13,r13,KPROBE_TEST_MAGIC_LOWER
+ l.addi r11,r13,0
+ l.jr r9
+ l.nop
+2: l.movhi r13,KPROBE_TEST_MAGIC_UPPER
+ l.movhi r15,hi(1b)
+ l.ori r15,r15,lo(1b)
+test_kprobes_jr_addr2:
+ l.jr r15
+ l.nop
+SYM_FUNC_END(test_kprobes_jr)
+
+SYM_FUNC_START(test_kprobes_jalr)
+ l.addi r15,r9,0
+ l.movhi r17,hi(2f)
+ l.ori r17,r17,lo(2f)
+test_kprobes_jalr_addr1:
+ l.jalr r17
+ l.nop
+1: l.movhi r13,KPROBE_TEST_MAGIC_UPPER
+ l.jr r9
+ l.nop
+2: l.movhi r17,hi(1b)
+ l.ori r17,r17,lo(1b)
+test_kprobes_jalr_addr2:
+ l.jalr r17
+ l.nop
+ l.ori r13,r13,KPROBE_TEST_MAGIC_LOWER
+ l.addi r11,r13,0
+ l.addi r9,r15,0
+ l.jr r9
+ l.nop
+SYM_FUNC_END(test_kprobes_jalr)
+
+SYM_FUNC_START(test_kprobes_branch)
+ l.addi r15,r0,1
+ l.addi r17,r0,0
+ l.sfgts r15,r17
+test_kprobes_branch_addr1:
+ l.bf 2f
+ l.nop
+1: l.ori r13,r13,KPROBE_TEST_MAGIC_LOWER
+ l.addi r11,r13,0
+ l.jr r9
+ l.nop
+2: l.movhi r13,KPROBE_TEST_MAGIC_UPPER
+ l.sfeq r15,r17
+test_kprobes_branch_addr2:
+ l.bnf 1b
+ l.nop
+SYM_FUNC_END(test_kprobes_branch)
+
+SYM_FUNC_START(test_kprobes_adrp)
+ l.addi r13,r0,0
+ l.addi r15,r9,0
+ l.addi r17,r0,-8192
+ l.jal 1f
+ l.nop
+1: l.and r19,r9,r17
+test_kprobes_adrp_addr:
+ l.adrp r21,0
+ l.sfeq r21,r19
+ l.bnf 2f
+ l.nop
+ l.movhi r13,KPROBE_TEST_MAGIC_UPPER
+ l.ori r13,r13,KPROBE_TEST_MAGIC_LOWER
+2: l.addi r9,r15,0
+ l.jr r9
+ l.nop
+SYM_FUNC_END(test_kprobes_adrp)
+
+ .section .rodata
+SYM_DATA_START(test_kprobes_addresses)
+ .word test_kprobes_j_addr1
+ .word test_kprobes_j_addr2
+ .word test_kprobes_jal_addr1
+ .word test_kprobes_jal_addr2
+ .word test_kprobes_jr_addr1
+ .word test_kprobes_jr_addr2
+ .word test_kprobes_jalr_addr1
+ .word test_kprobes_jalr_addr2
+ .word test_kprobes_branch_addr1
+ .word test_kprobes_branch_addr2
+ .word test_kprobes_adrp_addr
+ .word 0
+SYM_DATA_END(test_kprobes_addresses)
+
+ .section .rodata
+SYM_DATA_START(test_kprobes_functions)
+ .word test_kprobes_j
+ .word test_kprobes_jal
+ .word test_kprobes_jr
+ .word test_kprobes_jalr
+ .word test_kprobes_branch
+ .word test_kprobes_adrp
+ .word 0
+SYM_DATA_END(test_kprobes_functions)
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 4/4] openrisc: Add Kretprobes
2026-04-07 18:56 ` [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
` (4 preceding siblings ...)
2026-09-30 20:31 ` [PATCH v2 3/4] openrisc: Add unit tests for KProbes on branch instructions Sahil Siddiq
@ 2026-09-30 20:31 ` Sahil Siddiq
5 siblings, 0 replies; 15+ messages in thread
From: Sahil Siddiq @ 2026-09-30 20:31 UTC (permalink / raw)
To: jonas, stefan.kristiansson, shorne, naveen, davem, mhiramat
Cc: tytso, wangruikang, namcao, pjw, kees, linux-openrisc,
linux-kernel, linux-trace-kernel, Sahil Siddiq
Add Kretprobes support for OpenRISC. Kretprobes allow a probe to be
fired when a function returns. They are built on top of the generic
rethook framework and also require the KProbe mechanism introduced
in the previous commit. This work is primarily based on similar work
done for RISC-V.
Kretprobes work by inserting a KProbe at a function's entry point.
When the function begins to execute, the KProbe is fired and its
pre-handler replaces the function's return address with that of the
rethook trampoline. The trampoline in turn calls the kretprobe handler.
Control is returned to the original caller following this.
Signed-off-by: Sahil Siddiq <sahilcdq0@gmail.com>
---
arch/openrisc/Kconfig | 2 +
arch/openrisc/kernel/Makefile | 1 +
arch/openrisc/kernel/rethook.c | 32 ++++++++
arch/openrisc/kernel/rethook.h | 8 ++
arch/openrisc/kernel/rethook_trampoline.S | 97 +++++++++++++++++++++++
5 files changed, 140 insertions(+)
create mode 100644 arch/openrisc/kernel/rethook.c
create mode 100644 arch/openrisc/kernel/rethook.h
create mode 100644 arch/openrisc/kernel/rethook_trampoline.S
diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig
index d240533b424b..82365eb8f574 100644
--- a/arch/openrisc/Kconfig
+++ b/arch/openrisc/Kconfig
@@ -28,6 +28,8 @@ config OPENRISC
select HAVE_ARCH_JUMP_LABEL_RELATIVE
select HAVE_PCI
select HAVE_KPROBES
+ select HAVE_KRETPROBES
+ select HAVE_RETHOOK
select HAVE_UID16
select HAVE_PAGE_SIZE_8KB
select HAVE_REGS_AND_STACK_ACCESS_API
diff --git a/arch/openrisc/kernel/Makefile b/arch/openrisc/kernel/Makefile
index 0c1cade09e0c..e36537cea319 100644
--- a/arch/openrisc/kernel/Makefile
+++ b/arch/openrisc/kernel/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_OF) += prom.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_RETHOOK) += rethook.o rethook_trampoline.o
obj-$(CONFIG_OR_KPROBES_KUNIT) += tests/
obj-y += patching.o
diff --git a/arch/openrisc/kernel/rethook.c b/arch/openrisc/kernel/rethook.c
new file mode 100644
index 000000000000..50ef6715d044
--- /dev/null
+++ b/arch/openrisc/kernel/rethook.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Generic return hook for OpenRISC. Depends on KProbes.
+ *
+ * Linux architectural port borrowing liberally from similar works of
+ * others. All original copyrights apply as per the original source
+ * declaration.
+ *
+ * OpenRISC implementation:
+ * Copyright (C) 2026 Sahil Siddiq <sahilcdq0@gmail.com>
+ */
+
+#include <linux/kprobes.h>
+#include <linux/rethook.h>
+#include "rethook.h"
+
+unsigned long arch_rethook_trampoline_callback(struct pt_regs *regs)
+{
+ return rethook_trampoline_handler(regs, regs->gpr2);
+}
+NOKPROBE_SYMBOL(arch_rethook_trampoline_callback)
+
+void arch_rethook_prepare(struct rethook_node *rhn, struct pt_regs *regs, bool mcount)
+{
+ /* Store original return address and frame pointer */
+ rhn->ret_addr = regs->gpr9;
+ rhn->frame = regs->gpr2;
+
+ /* Set return address to trampoline */
+ regs->gpr9 = (unsigned long)arch_rethook_trampoline;
+}
+NOKPROBE_SYMBOL(arch_rethook_prepare)
diff --git a/arch/openrisc/kernel/rethook.h b/arch/openrisc/kernel/rethook.h
new file mode 100644
index 000000000000..332306a5c380
--- /dev/null
+++ b/arch/openrisc/kernel/rethook.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __OPENRISC_RETHOOK_H
+#define __OPENRISC_RETHOOK_H
+
+unsigned long arch_rethook_trampoline_callback(struct pt_regs *regs);
+void arch_rethook_prepare(struct rethook_node *rhn, struct pt_regs *regs, bool mcount);
+
+#endif
diff --git a/arch/openrisc/kernel/rethook_trampoline.S b/arch/openrisc/kernel/rethook_trampoline.S
new file mode 100644
index 000000000000..ac4ca0ecc669
--- /dev/null
+++ b/arch/openrisc/kernel/rethook_trampoline.S
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Author: Sahil Siddiq <sahilcdq0@gmail.com>
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/ptrace.h>
+
+ .text
+
+ .macro save_all_base_regs
+ l.sw PT_GPR2(r1),r2
+ l.sw PT_GPR3(r1),r3
+ l.sw PT_GPR4(r1),r4
+ l.sw PT_GPR5(r1),r5
+ l.sw PT_GPR6(r1),r6
+ l.sw PT_GPR7(r1),r7
+ l.sw PT_GPR8(r1),r8
+ l.sw PT_GPR9(r1),r9
+ l.sw PT_GPR10(r1),r10
+ l.sw PT_GPR11(r1),r11
+ l.sw PT_GPR12(r1),r12
+ l.sw PT_GPR13(r1),r13
+ l.sw PT_GPR14(r1),r14
+ l.sw PT_GPR15(r1),r15
+ l.sw PT_GPR16(r1),r16
+ l.sw PT_GPR17(r1),r17
+ l.sw PT_GPR18(r1),r18
+ l.sw PT_GPR19(r1),r19
+ l.sw PT_GPR20(r1),r20
+ l.sw PT_GPR21(r1),r21
+ l.sw PT_GPR22(r1),r22
+ l.sw PT_GPR23(r1),r23
+ l.sw PT_GPR24(r1),r24
+ l.sw PT_GPR25(r1),r25
+ l.sw PT_GPR26(r1),r26
+ l.sw PT_GPR27(r1),r27
+ l.sw PT_GPR28(r1),r28
+ l.sw PT_GPR29(r1),r29
+ l.sw PT_GPR30(r1),r30
+ l.sw PT_GPR31(r1),r31
+ .endm
+
+ .macro restore_all_base_regs
+ l.lwz r2,PT_GPR2(r1)
+ l.lwz r3,PT_GPR3(r1)
+ l.lwz r4,PT_GPR4(r1)
+ l.lwz r5,PT_GPR5(r1)
+ l.lwz r6,PT_GPR6(r1)
+ l.lwz r7,PT_GPR7(r1)
+ l.lwz r8,PT_GPR8(r1)
+ /* Return address in r9 is stale, so don't restore it */
+ l.lwz r10,PT_GPR10(r1)
+ l.lwz r11,PT_GPR11(r1)
+ l.lwz r12,PT_GPR12(r1)
+ l.lwz r13,PT_GPR13(r1)
+ l.lwz r14,PT_GPR14(r1)
+ l.lwz r15,PT_GPR15(r1)
+ l.lwz r16,PT_GPR16(r1)
+ l.lwz r17,PT_GPR17(r1)
+ l.lwz r18,PT_GPR18(r1)
+ l.lwz r19,PT_GPR19(r1)
+ l.lwz r20,PT_GPR20(r1)
+ l.lwz r21,PT_GPR21(r1)
+ l.lwz r22,PT_GPR22(r1)
+ l.lwz r23,PT_GPR23(r1)
+ l.lwz r24,PT_GPR24(r1)
+ l.lwz r25,PT_GPR25(r1)
+ l.lwz r26,PT_GPR26(r1)
+ l.lwz r27,PT_GPR27(r1)
+ l.lwz r28,PT_GPR28(r1)
+ l.lwz r29,PT_GPR29(r1)
+ l.lwz r30,PT_GPR30(r1)
+ l.lwz r31,PT_GPR31(r1)
+ .endm
+
+SYM_CODE_START(arch_rethook_trampoline)
+ /* Allocate enough space on stack for pt_regs */
+ l.addi r1,r1,-PT_SIZE
+ save_all_base_regs
+
+ l.addi r3,r1,0 /* pt_regs */
+ l.jal arch_rethook_trampoline_callback
+ l.nop
+
+ /* use the result as the return-address */
+ l.addi r9,r11,0
+
+ restore_all_base_regs
+ l.addi r1,r1,PT_SIZE
+
+ l.jr r9
+ l.nop
+SYM_CODE_END(arch_rethook_trampoline)
--
2.55.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 0/4] openrisc: Add support for KProbes
2026-09-30 20:31 ` [PATCH v2 0/4] openrisc: Add support for KProbes Sahil Siddiq
@ 2026-09-30 21:18 ` Sahil
0 siblings, 0 replies; 15+ messages in thread
From: Sahil @ 2026-09-30 21:18 UTC (permalink / raw)
To: jonas, stefan.kristiansson, shorne, naveen, davem, mhiramat
Cc: tytso, wangruikang, namcao, pjw, kees, linux-openrisc,
linux-kernel, linux-trace-kernel
On 10/1/26 2:01 AM, Sahil Siddiq wrote:
> Hi,
>
> Apologies for the delay. This is v2 of the patch series to add KProbe
> support for OpenRISC.
>
> The series also adds support for Kretprobes (which depends on the rethook
> framework [1]). Kretprobes are fired on function return.
>
> [...]
>
> I have also made changes based on the review comments I received for
> v1 [5] including several of Sachiko's comments [6]. I chose not to make
> changes in a few places. I'll send emails as a reply to this thread
> explaining my reasoning inline.
>
> [...]
>
> Sahil Siddiq (4):
> openrisc: Add utilities and clean up simulation of instructions
> openrisc: Add KProbes
> openrisc: Add unit tests for KProbes on branch instructions
> openrisc: Add Kretprobes
>
> [...]>
Sorry, I forgot to post a summary of the differences between v2 and v1:
Commit #1:
- arch/openrisc/include/asm/cpuinfo.h: Introduce "delay_slot" static key
- arch/openrisc/include/asm/insn-def.h:
- Use macros instead of union
- Use better names for macros
- Use genmask() for masks
- arch/openrisc/kernel/insn.c:
- Use macros instead of magic values
- (simulate_pc): Update PC
- (simulate_branch): Handle rb=r9 in l.jalr simulation
- arch/openrisc/kernel/setup.c: Use "delay slot" static key
- arch/openrisc/kernel/traps.c: Use macros instead of magic values
Commit #2:
- arch/openrisc/include/asm/kprobes.h:
- (struct prev_kprobe): Add restore_pc
- (struct kprobe_ctlblk): Move restore_pc from global "struct arch_specific_insn"
- arch/openrisc/kernel/kprobes.c:
- Use macros instead of magic values
- Use "get_kernel_nofault" instead of directly dereferencing address
- (insns_need_simulation): Use "daley slot" static key
- (arch_prepare_kprobe): Prevent KProbe insertion if instruction in delay slot also requires simulation
- (save_previous_kprobe): Use "restore_pc"
- (restore_previous_kprobe): Likewise
- (post_kprobe_handler): Don't re-enable preemption if KProbe is nested
- (setup_singlestep): Use "restore_pc"
- (reenter_kprobe): Return false in case of unrecoverable KProbe
- (kprobe_singlestep_handler): Enable interrupt in pt_regs->sr
- (kprobe_fault_handler): Likewise
Commit #3/#4: Introduced in v2 of the series
Thanks,
Sahil
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-09-30 21:19 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-07 18:56 [RFC 0/2] openrisc: Add support for KProbes Sahil Siddiq
2026-04-07 18:56 ` [RFC 1/2] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
2026-04-14 17:11 ` Stafford Horne
2026-04-15 6:10 ` Sahil
2026-04-15 6:39 ` Masami Hiramatsu
2026-04-16 4:57 ` Sahil
2026-09-30 20:31 ` [PATCH v2 0/4] openrisc: Add support for KProbes Sahil Siddiq
2026-09-30 21:18 ` Sahil
2026-09-30 20:31 ` [PATCH v2 1/4] openrisc: Add utilities and clean up simulation of instructions Sahil Siddiq
2026-09-30 20:31 ` [PATCH v2 2/4] openrisc: Add KProbes Sahil Siddiq
2026-09-30 20:31 ` [PATCH v2 3/4] openrisc: Add unit tests for KProbes on branch instructions Sahil Siddiq
2026-09-30 20:31 ` [PATCH v2 4/4] openrisc: Add Kretprobes Sahil Siddiq
2026-04-07 18:56 ` [RFC 2/2] openrisc: Add KProbes Sahil Siddiq
2026-04-15 6:48 ` [RFC 0/2] openrisc: Add support for KProbes Masami Hiramatsu
2026-04-16 5:00 ` Sahil
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®