You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
[S390] add kprobes support.
Signed-off-by: Michael Grundy <grundym@us.ibm.com> Signed-off-by: David Wilder <dwilder@us.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
committed by
Martin Schwidefsky
parent
5432114baf
commit
4ba069b802
@@ -487,8 +487,22 @@ source "drivers/net/Kconfig"
|
||||
|
||||
source "fs/Kconfig"
|
||||
|
||||
menu "Instrumentation Support"
|
||||
|
||||
source "arch/s390/oprofile/Kconfig"
|
||||
|
||||
config KPROBES
|
||||
bool "Kprobes (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && MODULES
|
||||
help
|
||||
Kprobes allows you to trap at almost any kernel address and
|
||||
execute a callback function. register_kprobe() establishes
|
||||
a probepoint and specifies the callback. Kprobes is useful
|
||||
for kernel debugging, non-intrusive instrumentation and testing.
|
||||
If in doubt, say "N".
|
||||
|
||||
endmenu
|
||||
|
||||
source "arch/s390/Kconfig.debug"
|
||||
|
||||
source "security/Kconfig"
|
||||
|
||||
@@ -24,6 +24,7 @@ obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \
|
||||
|
||||
obj-$(CONFIG_VIRT_TIMER) += vtime.o
|
||||
obj-$(CONFIG_STACKTRACE) += stacktrace.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
|
||||
# Kexec part
|
||||
S390_KEXEC_OBJS := machine_kexec.o crash.o
|
||||
|
||||
@@ -505,6 +505,8 @@ pgm_no_vtime2:
|
||||
mvc __THREAD_per+__PER_address(4,%r1),__LC_PER_ADDRESS
|
||||
mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID
|
||||
oi __TI_flags+3(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP
|
||||
tm SP_PSW+1(%r15),0x01 # kernel per event ?
|
||||
bz BASED(kernel_per)
|
||||
l %r3,__LC_PGM_ILC # load program interruption code
|
||||
la %r8,0x7f
|
||||
nr %r8,%r3 # clear per-event-bit and ilc
|
||||
@@ -536,6 +538,16 @@ pgm_no_vtime3:
|
||||
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
|
||||
b BASED(sysc_do_svc)
|
||||
|
||||
#
|
||||
# per was called from kernel, must be kprobes
|
||||
#
|
||||
kernel_per:
|
||||
mvi SP_TRAP+1(%r15),0x28 # set trap indication to pgm check
|
||||
la %r2,SP_PTREGS(%r15) # address of register-save area
|
||||
l %r1,BASED(.Lhandle_per) # load adr. of per handler
|
||||
la %r14,BASED(sysc_leave) # load adr. of system return
|
||||
br %r1 # branch to do_single_step
|
||||
|
||||
/*
|
||||
* IO interrupt handler routine
|
||||
*/
|
||||
|
||||
@@ -518,6 +518,8 @@ pgm_no_vtime2:
|
||||
#endif
|
||||
lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct
|
||||
lg %r1,__TI_task(%r9)
|
||||
tm SP_PSW+1(%r15),0x01 # kernel per event ?
|
||||
jz kernel_per
|
||||
mvc __THREAD_per+__PER_atmid(2,%r1),__LC_PER_ATMID
|
||||
mvc __THREAD_per+__PER_address(8,%r1),__LC_PER_ADDRESS
|
||||
mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID
|
||||
@@ -553,6 +555,16 @@ pgm_no_vtime3:
|
||||
stosm __SF_EMPTY(%r15),0x03 # reenable interrupts
|
||||
j sysc_do_svc
|
||||
|
||||
#
|
||||
# per was called from kernel, must be kprobes
|
||||
#
|
||||
kernel_per:
|
||||
lhi %r0,__LC_PGM_OLD_PSW
|
||||
sth %r0,SP_TRAP(%r15) # set trap indication to pgm check
|
||||
la %r2,SP_PTREGS(%r15) # address of register-save area
|
||||
larl %r14,sysc_leave # load adr. of system ret, no work
|
||||
jg do_single_step # branch to do_single_step
|
||||
|
||||
/*
|
||||
* IO interrupt handler routine
|
||||
*/
|
||||
|
||||
657
arch/s390/kernel/kprobes.c
Normal file
657
arch/s390/kernel/kprobes.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
@@ -39,6 +40,7 @@
|
||||
#include <asm/s390_ext.h>
|
||||
#include <asm/lowcore.h>
|
||||
#include <asm/debug.h>
|
||||
#include <asm/kdebug.h>
|
||||
|
||||
/* Called from entry.S only */
|
||||
extern void handle_per_exception(struct pt_regs *regs);
|
||||
@@ -74,6 +76,20 @@ static int kstack_depth_to_print = 12;
|
||||
static int kstack_depth_to_print = 20;
|
||||
#endif /* CONFIG_64BIT */
|
||||
|
||||
ATOMIC_NOTIFIER_HEAD(s390die_chain);
|
||||
|
||||
int register_die_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_register(&s390die_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(register_die_notifier);
|
||||
|
||||
int unregister_die_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_unregister(&s390die_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_die_notifier);
|
||||
|
||||
/*
|
||||
* For show_trace we have tree different stack to consider:
|
||||
* - the panic stack which is used if the kernel stack has overflown
|
||||
@@ -305,8 +321,9 @@ report_user_fault(long interruption_code, struct pt_regs *regs)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void inline do_trap(long interruption_code, int signr, char *str,
|
||||
struct pt_regs *regs, siginfo_t *info)
|
||||
static void __kprobes inline do_trap(long interruption_code, int signr,
|
||||
char *str, struct pt_regs *regs,
|
||||
siginfo_t *info)
|
||||
{
|
||||
/*
|
||||
* We got all needed information from the lowcore and can
|
||||
@@ -315,6 +332,10 @@ static void inline do_trap(long interruption_code, int signr, char *str,
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE)
|
||||
local_irq_enable();
|
||||
|
||||
if (notify_die(DIE_TRAP, str, regs, interruption_code,
|
||||
interruption_code, signr) == NOTIFY_STOP)
|
||||
return;
|
||||
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
@@ -336,8 +357,12 @@ static inline void __user *get_check_address(struct pt_regs *regs)
|
||||
return (void __user *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN);
|
||||
}
|
||||
|
||||
void do_single_step(struct pt_regs *regs)
|
||||
void __kprobes do_single_step(struct pt_regs *regs)
|
||||
{
|
||||
if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0,
|
||||
SIGTRAP) == NOTIFY_STOP){
|
||||
return;
|
||||
}
|
||||
if ((current->ptrace & PT_PTRACED) != 0)
|
||||
force_sig(SIGTRAP, current);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ SECTIONS
|
||||
*(.text)
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
KPROBES_TEXT
|
||||
*(.fixup)
|
||||
*(.gnu.warning)
|
||||
} = 0x0700
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
#include <linux/console.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/kdebug.h>
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
#define __FAIL_ADDR_MASK 0x7ffff000
|
||||
@@ -48,6 +50,38 @@ extern int sysctl_userprocess_debug;
|
||||
|
||||
extern void die(const char *,struct pt_regs *,long);
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
|
||||
int register_page_fault_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_register(¬ify_page_fault_chain, nb);
|
||||
}
|
||||
|
||||
int unregister_page_fault_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return atomic_notifier_chain_unregister(¬ify_page_fault_chain, nb);
|
||||
}
|
||||
|
||||
static inline int notify_page_fault(enum die_val val, const char *str,
|
||||
struct pt_regs *regs, long err, int trap, int sig)
|
||||
{
|
||||
struct die_args args = {
|
||||
.regs = regs,
|
||||
.str = str,
|
||||
.err = err,
|
||||
.trapnr = trap,
|
||||
.signr = sig
|
||||
};
|
||||
return atomic_notifier_call_chain(¬ify_page_fault_chain, val, &args);
|
||||
}
|
||||
#else
|
||||
static inline int notify_page_fault(enum die_val val, const char *str,
|
||||
struct pt_regs *regs, long err, int trap, int sig)
|
||||
{
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern spinlock_t timerlist_lock;
|
||||
|
||||
/*
|
||||
@@ -159,7 +193,7 @@ static void do_sigsegv(struct pt_regs *regs, unsigned long error_code,
|
||||
* 11 Page translation -> Not present (nullification)
|
||||
* 3b Region third trans. -> Not present (nullification)
|
||||
*/
|
||||
static inline void
|
||||
static inline void __kprobes
|
||||
do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
@@ -173,6 +207,10 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection)
|
||||
tsk = current;
|
||||
mm = tsk->mm;
|
||||
|
||||
if (notify_page_fault(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
|
||||
SIGSEGV) == NOTIFY_STOP)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check for low-address protection. This needs to be treated
|
||||
* as a special case because the translation exception code
|
||||
|
||||
59
include/asm-s390/kdebug.h
Normal file
59
include/asm-s390/kdebug.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef _S390_KDEBUG_H
|
||||
#define _S390_KDEBUG_H
|
||||
|
||||
/*
|
||||
* Feb 2006 Ported to s390 <grundym@us.ibm.com>
|
||||
*/
|
||||
#include <linux/notifier.h>
|
||||
|
||||
struct pt_regs;
|
||||
|
||||
struct die_args {
|
||||
struct pt_regs *regs;
|
||||
const char *str;
|
||||
long err;
|
||||
int trapnr;
|
||||
int signr;
|
||||
};
|
||||
|
||||
/* Note - you should never unregister because that can race with NMIs.
|
||||
* If you really want to do it first unregister - then synchronize_sched
|
||||
* - then free.
|
||||
*/
|
||||
extern int register_die_notifier(struct notifier_block *);
|
||||
extern int unregister_die_notifier(struct notifier_block *);
|
||||
extern int register_page_fault_notifier(struct notifier_block *);
|
||||
extern int unregister_page_fault_notifier(struct notifier_block *);
|
||||
extern struct atomic_notifier_head s390die_chain;
|
||||
|
||||
|
||||
enum die_val {
|
||||
DIE_OOPS = 1,
|
||||
DIE_BPT,
|
||||
DIE_SSTEP,
|
||||
DIE_PANIC,
|
||||
DIE_NMI,
|
||||
DIE_DIE,
|
||||
DIE_NMIWATCHDOG,
|
||||
DIE_KERNELDEBUG,
|
||||
DIE_TRAP,
|
||||
DIE_GPF,
|
||||
DIE_CALL,
|
||||
DIE_NMI_IPI,
|
||||
DIE_PAGE_FAULT,
|
||||
};
|
||||
|
||||
static inline int notify_die(enum die_val val, const char *str,
|
||||
struct pt_regs *regs, long err, int trap, int sig)
|
||||
{
|
||||
struct die_args args = {
|
||||
.regs = regs,
|
||||
.str = str,
|
||||
.err = err,
|
||||
.trapnr = trap,
|
||||
.signr = sig
|
||||
};
|
||||
return atomic_notifier_call_chain(&s390die_chain, val, &args);
|
||||
}
|
||||
|
||||
#endif
|
||||
114
include/asm-s390/kprobes.h
Normal file
114
include/asm-s390/kprobes.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#ifndef _ASM_S390_KPROBES_H
|
||||
#define _ASM_S390_KPROBES_H
|
||||
/*
|
||||
* Kernel Probes (KProbes)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2002, 2006
|
||||
*
|
||||
* 2002-Oct Created by Vamsi Krishna S <vamsi_krishna@in.ibm.com> Kernel
|
||||
* Probes initial implementation ( includes suggestions from
|
||||
* Rusty Russell).
|
||||
* 2004-Nov Modified for PPC64 by Ananth N Mavinakayanahalli
|
||||
* <ananth@in.ibm.com>
|
||||
* 2005-Dec Used as a template for s390 by Mike Grundy
|
||||
* <grundym@us.ibm.com>
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/percpu.h>
|
||||
|
||||
#define __ARCH_WANT_KPROBES_INSN_SLOT
|
||||
struct pt_regs;
|
||||
struct kprobe;
|
||||
|
||||
typedef u16 kprobe_opcode_t;
|
||||
#define BREAKPOINT_INSTRUCTION 0x0002
|
||||
|
||||
/* Maximum instruction size is 3 (16bit) halfwords: */
|
||||
#define MAX_INSN_SIZE 0x0003
|
||||
#define MAX_STACK_SIZE 64
|
||||
#define MIN_STACK_SIZE(ADDR) (((MAX_STACK_SIZE) < \
|
||||
(((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR))) \
|
||||
? (MAX_STACK_SIZE) \
|
||||
: (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
|
||||
|
||||
#define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)(pentry)
|
||||
|
||||
#define ARCH_SUPPORTS_KRETPROBES
|
||||
#define ARCH_INACTIVE_KPROBE_COUNT 0
|
||||
|
||||
#define KPROBE_SWAP_INST 0x10
|
||||
|
||||
#define FIXUP_PSW_NORMAL 0x08
|
||||
#define FIXUP_BRANCH_NOT_TAKEN 0x04
|
||||
#define FIXUP_RETURN_REGISTER 0x02
|
||||
#define FIXUP_NOT_REQUIRED 0x01
|
||||
|
||||
/* Architecture specific copy of original instruction */
|
||||
struct arch_specific_insn {
|
||||
/* copy of original instruction */
|
||||
kprobe_opcode_t *insn;
|
||||
int fixup;
|
||||
int ilen;
|
||||
int reg;
|
||||
};
|
||||
|
||||
struct ins_replace_args {
|
||||
kprobe_opcode_t *ptr;
|
||||
kprobe_opcode_t old;
|
||||
kprobe_opcode_t new;
|
||||
};
|
||||
struct prev_kprobe {
|
||||
struct kprobe *kp;
|
||||
unsigned long status;
|
||||
unsigned long saved_psw;
|
||||
unsigned long kprobe_saved_imask;
|
||||
unsigned long kprobe_saved_ctl[3];
|
||||
};
|
||||
|
||||
/* per-cpu kprobe control block */
|
||||
struct kprobe_ctlblk {
|
||||
unsigned long kprobe_status;
|
||||
unsigned long kprobe_saved_imask;
|
||||
unsigned long kprobe_saved_ctl[3];
|
||||
struct pt_regs jprobe_saved_regs;
|
||||
unsigned long jprobe_saved_r14;
|
||||
unsigned long jprobe_saved_r15;
|
||||
struct prev_kprobe prev_kprobe;
|
||||
kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE];
|
||||
};
|
||||
|
||||
void arch_remove_kprobe(struct kprobe *p);
|
||||
void kretprobe_trampoline(void);
|
||||
int is_prohibited_opcode(kprobe_opcode_t *instruction);
|
||||
void get_instruction_type(struct arch_specific_insn *ainsn);
|
||||
|
||||
#define flush_insn_slot(p) do { } while (0)
|
||||
|
||||
#endif /* _ASM_S390_KPROBES_H */
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
|
||||
extern int kprobe_exceptions_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data);
|
||||
#else /* !CONFIG_KPROBES */
|
||||
static inline int kprobe_exceptions_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user