Skip to content

Commit

Permalink
csky: Add kprobes supported
Browse files Browse the repository at this point in the history
This patch enable kprobes, kretprobes, ftrace interface. It utilized
software breakpoint and single step debug exceptions, instructions
simulation on csky.

We use USR_BKPT replace origin instruction, and the kprobe handler
prepares an excutable memory slot for out-of-line execution with a
copy of the original instruction being probed. Most of instructions
could be executed by single-step, but some instructions need origin
pc value to execute and we need software simulate these instructions.

Signed-off-by: Guo Ren <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Steven Rostedt (VMware) <[email protected]>
  • Loading branch information
guoren83 committed Apr 3, 2020
1 parent 000591f commit 33e53ae
Show file tree
Hide file tree
Showing 16 changed files with 1,197 additions and 2 deletions.
3 changes: 3 additions & 0 deletions arch/csky/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ config CSKY
select HAVE_KERNEL_GZIP
select HAVE_KERNEL_LZO
select HAVE_KERNEL_LZMA
select HAVE_KPROBES if !CPU_CK610
select HAVE_KPROBES_ON_FTRACE if !CPU_CK610
select HAVE_KRETPROBES if !CPU_CK610
select HAVE_PERF_EVENTS
select HAVE_PERF_REGS
select HAVE_PERF_USER_STACK_DUMP
Expand Down
1 change: 0 additions & 1 deletion arch/csky/include/asm/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ generic-y += irq_regs.h
generic-y += irq_work.h
generic-y += kdebug.h
generic-y += kmap_types.h
generic-y += kprobes.h
generic-y += kvm_para.h
generic-y += linkage.h
generic-y += local.h
Expand Down
48 changes: 48 additions & 0 deletions arch/csky/include/asm/kprobes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* SPDX-License-Identifier: GPL-2.0-only */

#ifndef __ASM_CSKY_KPROBES_H
#define __ASM_CSKY_KPROBES_H

#include <asm-generic/kprobes.h>

#ifdef CONFIG_KPROBES
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/percpu.h>

#define __ARCH_WANT_KPROBES_INSN_SLOT
#define MAX_INSN_SIZE 1

#define flush_insn_slot(p) do { } while (0)
#define kretprobe_blacklist_size 0

#include <asm/probes.h>

struct prev_kprobe {
struct kprobe *kp;
unsigned int status;
};

/* Single step context for kprobe */
struct kprobe_step_ctx {
unsigned long ss_pending;
unsigned long match_addr;
};

/* per-cpu kprobe control block */
struct kprobe_ctlblk {
unsigned int kprobe_status;
unsigned long saved_sr;
struct prev_kprobe prev_kprobe;
struct kprobe_step_ctx ss_ctx;
};

void arch_remove_kprobe(struct kprobe *p);
int kprobe_fault_handler(struct pt_regs *regs, unsigned int trapnr);
int kprobe_breakpoint_handler(struct pt_regs *regs);
int kprobe_single_step_handler(struct pt_regs *regs);
void kretprobe_trampoline(void);
void __kprobes *trampoline_probe_handler(struct pt_regs *regs);

#endif /* CONFIG_KPROBES */
#endif /* __ASM_CSKY_KPROBES_H */
24 changes: 24 additions & 0 deletions arch/csky/include/asm/probes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef __ASM_CSKY_PROBES_H
#define __ASM_CSKY_PROBES_H

typedef u32 probe_opcode_t;
typedef void (probes_handler_t) (u32 opcode, long addr, struct pt_regs *);

/* architecture specific copy of original instruction */
struct arch_probe_insn {
probe_opcode_t *insn;
probes_handler_t *handler;
/* restore address after simulation */
unsigned long restore;
};

#ifdef CONFIG_KPROBES
typedef u32 kprobe_opcode_t;
struct arch_specific_insn {
struct arch_probe_insn api;
};
#endif

#endif /* __ASM_CSKY_PROBES_H */
2 changes: 2 additions & 0 deletions arch/csky/include/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#define PS_S 0x80000000 /* Supervisor Mode */

#define USR_BKPT 0x1464

#define arch_has_single_step() (1)
#define current_pt_regs() \
({ (struct pt_regs *)((char *)current_thread_info() + THREAD_SIZE) - 1; })
Expand Down
1 change: 1 addition & 0 deletions arch/csky/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extra-y := head.o vmlinux.lds
obj-y += entry.o atomic.o signal.o traps.o irq.o time.o vdso.o
obj-y += power.o syscall.o syscall_table.o setup.o
obj-y += process.o cpu-probe.o ptrace.o dumpstack.o
obj-y += probes/

obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_SMP) += smp.o
Expand Down
6 changes: 6 additions & 0 deletions arch/csky/kernel/probes/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o simulate-insn.o
obj-$(CONFIG_KPROBES) += kprobes_trampoline.o
obj-$(CONFIG_KPROBES_ON_FTRACE) += ftrace.o

CFLAGS_REMOVE_simulate-insn.o = $(CC_FLAGS_FTRACE)
49 changes: 49 additions & 0 deletions arch/csky/kernel/probes/decode-insn.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0+

#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/module.h>
#include <linux/kallsyms.h>
#include <asm/sections.h>

#include "decode-insn.h"
#include "simulate-insn.h"

/* Return:
* INSN_REJECTED If instruction is one not allowed to kprobe,
* INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
*/
enum probe_insn __kprobes
csky_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *api)
{
probe_opcode_t insn = le32_to_cpu(*addr);

CSKY_INSN_SET_SIMULATE(br16, insn);
CSKY_INSN_SET_SIMULATE(bt16, insn);
CSKY_INSN_SET_SIMULATE(bf16, insn);
CSKY_INSN_SET_SIMULATE(jmp16, insn);
CSKY_INSN_SET_SIMULATE(jsr16, insn);
CSKY_INSN_SET_SIMULATE(lrw16, insn);
CSKY_INSN_SET_SIMULATE(pop16, insn);

CSKY_INSN_SET_SIMULATE(br32, insn);
CSKY_INSN_SET_SIMULATE(bt32, insn);
CSKY_INSN_SET_SIMULATE(bf32, insn);
CSKY_INSN_SET_SIMULATE(jmp32, insn);
CSKY_INSN_SET_SIMULATE(jsr32, insn);
CSKY_INSN_SET_SIMULATE(lrw32, insn);
CSKY_INSN_SET_SIMULATE(pop32, insn);

CSKY_INSN_SET_SIMULATE(bez32, insn);
CSKY_INSN_SET_SIMULATE(bnez32, insn);
CSKY_INSN_SET_SIMULATE(bnezad32, insn);
CSKY_INSN_SET_SIMULATE(bhsz32, insn);
CSKY_INSN_SET_SIMULATE(bhz32, insn);
CSKY_INSN_SET_SIMULATE(blsz32, insn);
CSKY_INSN_SET_SIMULATE(blz32, insn);
CSKY_INSN_SET_SIMULATE(bsr32, insn);
CSKY_INSN_SET_SIMULATE(jmpi32, insn);
CSKY_INSN_SET_SIMULATE(jsri32, insn);

return INSN_GOOD;
}
20 changes: 20 additions & 0 deletions arch/csky/kernel/probes/decode-insn.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0+ */

#ifndef __CSKY_KERNEL_KPROBES_DECODE_INSN_H
#define __CSKY_KERNEL_KPROBES_DECODE_INSN_H

#include <asm/sections.h>
#include <asm/kprobes.h>

enum probe_insn {
INSN_REJECTED,
INSN_GOOD_NO_SLOT,
INSN_GOOD,
};

#define is_insn32(insn) ((insn & 0xc000) == 0xc000)

enum probe_insn __kprobes
csky_probe_decode_insn(probe_opcode_t *addr, struct arch_probe_insn *asi);

#endif /* __CSKY_KERNEL_KPROBES_DECODE_INSN_H */
66 changes: 66 additions & 0 deletions arch/csky/kernel/probes/ftrace.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0

#include <linux/kprobes.h>

int arch_check_ftrace_location(struct kprobe *p)
{
if (ftrace_location((unsigned long)p->addr))
p->flags |= KPROBE_FLAG_FTRACE;
return 0;
}

/* Ftrace callback handler for kprobes -- called under preepmt disabed */
void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *ops, struct pt_regs *regs)
{
bool lr_saver = false;
struct kprobe *p;
struct kprobe_ctlblk *kcb;

/* Preempt is disabled by ftrace */
p = get_kprobe((kprobe_opcode_t *)ip);
if (!p) {
p = get_kprobe((kprobe_opcode_t *)(ip - MCOUNT_INSN_SIZE));
if (unlikely(!p) || kprobe_disabled(p))
return;
lr_saver = true;
}

kcb = get_kprobe_ctlblk();
if (kprobe_running()) {
kprobes_inc_nmissed_count(p);
} else {
unsigned long orig_ip = instruction_pointer(regs);

if (lr_saver)
ip -= MCOUNT_INSN_SIZE;
instruction_pointer_set(regs, ip);
__this_cpu_write(current_kprobe, p);
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
if (!p->pre_handler || !p->pre_handler(p, regs)) {
/*
* Emulate singlestep (and also recover regs->pc)
* as if there is a nop
*/
instruction_pointer_set(regs,
(unsigned long)p->addr + MCOUNT_INSN_SIZE);
if (unlikely(p->post_handler)) {
kcb->kprobe_status = KPROBE_HIT_SSDONE;
p->post_handler(p, regs, 0);
}
instruction_pointer_set(regs, orig_ip);
}
/*
* If pre_handler returns !0, it changes regs->pc. We have to
* skip emulating post_handler.
*/
__this_cpu_write(current_kprobe, NULL);
}
}
NOKPROBE_SYMBOL(kprobe_ftrace_handler);

int arch_prepare_kprobe_ftrace(struct kprobe *p)
{
p->ainsn.api.insn = NULL;
return 0;
}
Loading

0 comments on commit 33e53ae

Please sign in to comment.