Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
40e084a5
Commit
40e084a5
authored
Jul 29, 2015
by
Ralf Baechle
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MIPS: Add uprobes support.
Signed-off-by:
Ralf Baechle
<
ralf@linux-mips.org
>
parent
e3b28831
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
597 additions
and
2 deletions
+597
-2
arch/mips/Kconfig
arch/mips/Kconfig
+4
-0
arch/mips/include/asm/kdebug.h
arch/mips/include/asm/kdebug.h
+3
-1
arch/mips/include/asm/ptrace.h
arch/mips/include/asm/ptrace.h
+80
-0
arch/mips/include/asm/thread_info.h
arch/mips/include/asm/thread_info.h
+4
-1
arch/mips/include/asm/uprobes.h
arch/mips/include/asm/uprobes.h
+58
-0
arch/mips/include/uapi/asm/break.h
arch/mips/include/uapi/asm/break.h
+2
-0
arch/mips/kernel/Makefile
arch/mips/kernel/Makefile
+1
-0
arch/mips/kernel/ptrace.c
arch/mips/kernel/ptrace.c
+88
-0
arch/mips/kernel/signal.c
arch/mips/kernel/signal.c
+4
-0
arch/mips/kernel/traps.c
arch/mips/kernel/traps.c
+12
-0
arch/mips/kernel/uprobes.c
arch/mips/kernel/uprobes.c
+341
-0
No files found.
arch/mips/Kconfig
View file @
40e084a5
config MIPS
config MIPS
bool
bool
default y
default y
select ARCH_SUPPORTS_UPROBES
select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_MIGHT_HAVE_PC_SERIO
select ARCH_MIGHT_HAVE_PC_SERIO
select ARCH_USE_CMPXCHG_LOCKREF if 64BIT
select ARCH_USE_CMPXCHG_LOCKREF if 64BIT
...
@@ -1041,6 +1042,9 @@ config FW_CFE
...
@@ -1041,6 +1042,9 @@ config FW_CFE
config ARCH_DMA_ADDR_T_64BIT
config ARCH_DMA_ADDR_T_64BIT
def_bool (HIGHMEM && ARCH_PHYS_ADDR_T_64BIT) || 64BIT
def_bool (HIGHMEM && ARCH_PHYS_ADDR_T_64BIT) || 64BIT
config ARCH_SUPPORTS_UPROBES
bool
config DMA_MAYBE_COHERENT
config DMA_MAYBE_COHERENT
select DMA_NONCOHERENT
select DMA_NONCOHERENT
bool
bool
...
...
arch/mips/include/asm/kdebug.h
View file @
40e084a5
...
@@ -11,7 +11,9 @@ enum die_val {
...
@@ -11,7 +11,9 @@ enum die_val {
DIE_PAGE_FAULT
,
DIE_PAGE_FAULT
,
DIE_BREAK
,
DIE_BREAK
,
DIE_SSTEPBP
,
DIE_SSTEPBP
,
DIE_MSAFP
DIE_MSAFP
,
DIE_UPROBE
,
DIE_UPROBE_XOL
,
};
};
#endif
/* _ASM_MIPS_KDEBUG_H */
#endif
/* _ASM_MIPS_KDEBUG_H */
arch/mips/include/asm/ptrace.h
View file @
40e084a5
...
@@ -14,11 +14,16 @@
...
@@ -14,11 +14,16 @@
#include <linux/linkage.h>
#include <linux/linkage.h>
#include <linux/types.h>
#include <linux/types.h>
#include <asm/isadep.h>
#include <asm/isadep.h>
#include <asm/page.h>
#include <asm/thread_info.h>
#include <uapi/asm/ptrace.h>
#include <uapi/asm/ptrace.h>
/*
/*
* This struct defines the way the registers are stored on the stack during a
* This struct defines the way the registers are stored on the stack during a
* system call/exception. As usual the registers k0/k1 aren't being saved.
* system call/exception. As usual the registers k0/k1 aren't being saved.
*
* If you add a register here, also add it to regoffset_table[] in
* arch/mips/kernel/ptrace.c.
*/
*/
struct
pt_regs
{
struct
pt_regs
{
#ifdef CONFIG_32BIT
#ifdef CONFIG_32BIT
...
@@ -43,8 +48,83 @@ struct pt_regs {
...
@@ -43,8 +48,83 @@ struct pt_regs {
unsigned
long
long
mpl
[
6
];
/* MTM{0-5} */
unsigned
long
long
mpl
[
6
];
/* MTM{0-5} */
unsigned
long
long
mtp
[
6
];
/* MTP{0-5} */
unsigned
long
long
mtp
[
6
];
/* MTP{0-5} */
#endif
#endif
unsigned
long
__last
[
0
];
}
__aligned
(
8
);
}
__aligned
(
8
);
static
inline
unsigned
long
kernel_stack_pointer
(
struct
pt_regs
*
regs
)
{
return
regs
->
regs
[
31
];
}
/*
* Don't use asm-generic/ptrace.h it defines FP accessors that don't make
* sense on MIPS. We rather want an error if they get invoked.
*/
static
inline
void
instruction_pointer_set
(
struct
pt_regs
*
regs
,
unsigned
long
val
)
{
regs
->
cp0_epc
=
val
;
}
/* Query offset/name of register from its name/offset */
extern
int
regs_query_register_offset
(
const
char
*
name
);
#define MAX_REG_OFFSET (offsetof(struct pt_regs, __last))
/**
* regs_get_register() - get register value from its offset
* @regs: pt_regs from which register value is gotten.
* @offset: offset number of the register.
*
* regs_get_register returns the value of a register. The @offset is the
* offset of the register in struct pt_regs address which specified by @regs.
* If @offset is bigger than MAX_REG_OFFSET, this returns 0.
*/
static
inline
unsigned
long
regs_get_register
(
struct
pt_regs
*
regs
,
unsigned
int
offset
)
{
if
(
unlikely
(
offset
>
MAX_REG_OFFSET
))
return
0
;
return
*
(
unsigned
long
*
)((
unsigned
long
)
regs
+
offset
);
}
/**
* regs_within_kernel_stack() - check the address in the stack
* @regs: pt_regs which contains kernel stack pointer.
* @addr: address which is checked.
*
* regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
* If @addr is within the kernel stack, it returns true. If not, returns false.
*/
static
inline
int
regs_within_kernel_stack
(
struct
pt_regs
*
regs
,
unsigned
long
addr
)
{
return
((
addr
&
~
(
THREAD_SIZE
-
1
))
==
(
kernel_stack_pointer
(
regs
)
&
~
(
THREAD_SIZE
-
1
)));
}
/**
* regs_get_kernel_stack_nth() - get Nth entry of the stack
* @regs: pt_regs which contains kernel stack pointer.
* @n: stack entry number.
*
* regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
* is specified by @regs. If the @n th entry is NOT in the kernel stack,
* this returns 0.
*/
static
inline
unsigned
long
regs_get_kernel_stack_nth
(
struct
pt_regs
*
regs
,
unsigned
int
n
)
{
unsigned
long
*
addr
=
(
unsigned
long
*
)
kernel_stack_pointer
(
regs
);
addr
+=
n
;
if
(
regs_within_kernel_stack
(
regs
,
(
unsigned
long
)
addr
))
return
*
addr
;
else
return
0
;
}
struct
task_struct
;
struct
task_struct
;
extern
int
ptrace_getregs
(
struct
task_struct
*
child
,
extern
int
ptrace_getregs
(
struct
task_struct
*
child
,
...
...
arch/mips/include/asm/thread_info.h
View file @
40e084a5
...
@@ -99,6 +99,7 @@ static inline struct thread_info *current_thread_info(void)
...
@@ -99,6 +99,7 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_SYSCALL_AUDIT 3
/* syscall auditing active */
#define TIF_SYSCALL_AUDIT 3
/* syscall auditing active */
#define TIF_SECCOMP 4
/* secure computing */
#define TIF_SECCOMP 4
/* secure computing */
#define TIF_NOTIFY_RESUME 5
/* callback before returning to user */
#define TIF_NOTIFY_RESUME 5
/* callback before returning to user */
#define TIF_UPROBE 6
/* breakpointed or singlestepping */
#define TIF_RESTORE_SIGMASK 9
/* restore signal mask in do_signal() */
#define TIF_RESTORE_SIGMASK 9
/* restore signal mask in do_signal() */
#define TIF_USEDFPU 16
/* FPU was used by this task this quantum (SMP) */
#define TIF_USEDFPU 16
/* FPU was used by this task this quantum (SMP) */
#define TIF_MEMDIE 18
/* is terminating due to OOM killer */
#define TIF_MEMDIE 18
/* is terminating due to OOM killer */
...
@@ -122,6 +123,7 @@ static inline struct thread_info *current_thread_info(void)
...
@@ -122,6 +123,7 @@ static inline struct thread_info *current_thread_info(void)
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
#define _TIF_SECCOMP (1<<TIF_SECCOMP)
#define _TIF_SECCOMP (1<<TIF_SECCOMP)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_UPROBE (1<<TIF_UPROBE)
#define _TIF_USEDFPU (1<<TIF_USEDFPU)
#define _TIF_USEDFPU (1<<TIF_USEDFPU)
#define _TIF_NOHZ (1<<TIF_NOHZ)
#define _TIF_NOHZ (1<<TIF_NOHZ)
#define _TIF_FIXADE (1<<TIF_FIXADE)
#define _TIF_FIXADE (1<<TIF_FIXADE)
...
@@ -146,7 +148,8 @@ static inline struct thread_info *current_thread_info(void)
...
@@ -146,7 +148,8 @@ static inline struct thread_info *current_thread_info(void)
/* work to do on interrupt/exception return */
/* work to do on interrupt/exception return */
#define _TIF_WORK_MASK \
#define _TIF_WORK_MASK \
(_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_NOTIFY_RESUME)
(_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_NOTIFY_RESUME | \
_TIF_UPROBE)
/* work to do on any return to u-space */
/* work to do on any return to u-space */
#define _TIF_ALLWORK_MASK (_TIF_NOHZ | _TIF_WORK_MASK | \
#define _TIF_ALLWORK_MASK (_TIF_NOHZ | _TIF_WORK_MASK | \
_TIF_WORK_SYSCALL_EXIT | \
_TIF_WORK_SYSCALL_EXIT | \
...
...
arch/mips/include/asm/uprobes.h
0 → 100644
View file @
40e084a5
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#ifndef __ASM_UPROBES_H
#define __ASM_UPROBES_H
#include <linux/notifier.h>
#include <linux/types.h>
#include <asm/break.h>
#include <asm/inst.h>
/*
* We want this to be defined as union mips_instruction but that makes the
* generic code blow up.
*/
typedef
u32
uprobe_opcode_t
;
/*
* Classic MIPS (note this implementation doesn't consider microMIPS yet)
* instructions are always 4 bytes but in order to deal with branches and
* their delay slots, we treat instructions as having 8 bytes maximum.
*/
#define MAX_UINSN_BYTES 8
#define UPROBE_XOL_SLOT_BYTES 128
/* Max. cache line size */
#define UPROBE_BRK_UPROBE 0x000d000d
/* break 13 */
#define UPROBE_BRK_UPROBE_XOL 0x000e000d
/* break 14 */
#define UPROBE_SWBP_INSN UPROBE_BRK_UPROBE
#define UPROBE_SWBP_INSN_SIZE 4
struct
arch_uprobe
{
unsigned
long
resume_epc
;
u32
insn
[
2
];
u32
ixol
[
2
];
union
mips_instruction
orig_inst
[
MAX_UINSN_BYTES
/
4
];
};
struct
arch_uprobe_task
{
unsigned
long
saved_trap_nr
;
};
extern
int
arch_uprobe_analyze_insn
(
struct
arch_uprobe
*
aup
,
struct
mm_struct
*
mm
,
unsigned
long
addr
);
extern
int
arch_uprobe_pre_xol
(
struct
arch_uprobe
*
aup
,
struct
pt_regs
*
regs
);
extern
int
arch_uprobe_post_xol
(
struct
arch_uprobe
*
aup
,
struct
pt_regs
*
regs
);
extern
bool
arch_uprobe_xol_was_trapped
(
struct
task_struct
*
tsk
);
extern
int
arch_uprobe_exception_notify
(
struct
notifier_block
*
self
,
unsigned
long
val
,
void
*
data
);
extern
void
arch_uprobe_abort_xol
(
struct
arch_uprobe
*
aup
,
struct
pt_regs
*
regs
);
extern
unsigned
long
arch_uretprobe_hijack_return_addr
(
unsigned
long
trampoline_vaddr
,
struct
pt_regs
*
regs
);
#endif
/* __ASM_UPROBES_H */
arch/mips/include/uapi/asm/break.h
View file @
40e084a5
...
@@ -21,6 +21,8 @@
...
@@ -21,6 +21,8 @@
#define BRK_DIVZERO 7
/* Divide by zero check */
#define BRK_DIVZERO 7
/* Divide by zero check */
#define BRK_RANGE 8
/* Range error check */
#define BRK_RANGE 8
/* Range error check */
#define BRK_BUG 12
/* Used by BUG() */
#define BRK_BUG 12
/* Used by BUG() */
#define BRK_UPROBE 13
/* See <asm/uprobes.h> */
#define BRK_UPROBE_XOL 14
/* See <asm/uprobes.h> */
#define BRK_MEMU 514
/* Used by FPU emulator */
#define BRK_MEMU 514
/* Used by FPU emulator */
#define BRK_KPROBE_BP 515
/* Kprobe break */
#define BRK_KPROBE_BP 515
/* Kprobe break */
#define BRK_KPROBE_SSTEPBP 516
/* Kprobe single step software implementation */
#define BRK_KPROBE_SSTEPBP 516
/* Kprobe single step software implementation */
...
...
arch/mips/kernel/Makefile
View file @
40e084a5
...
@@ -100,6 +100,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_event.o
...
@@ -100,6 +100,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_event.o
obj-$(CONFIG_HW_PERF_EVENTS)
+=
perf_event_mipsxx.o
obj-$(CONFIG_HW_PERF_EVENTS)
+=
perf_event_mipsxx.o
obj-$(CONFIG_JUMP_LABEL)
+=
jump_label.o
obj-$(CONFIG_JUMP_LABEL)
+=
jump_label.o
obj-$(CONFIG_UPROBES)
+=
uprobes.o
obj-$(CONFIG_MIPS_CM)
+=
mips-cm.o
obj-$(CONFIG_MIPS_CM)
+=
mips-cm.o
obj-$(CONFIG_MIPS_CPC)
+=
mips-cpc.o
obj-$(CONFIG_MIPS_CPC)
+=
mips-cpc.o
...
...
arch/mips/kernel/ptrace.c
View file @
40e084a5
...
@@ -25,6 +25,7 @@
...
@@ -25,6 +25,7 @@
#include <linux/regset.h>
#include <linux/regset.h>
#include <linux/smp.h>
#include <linux/smp.h>
#include <linux/security.h>
#include <linux/security.h>
#include <linux/stddef.h>
#include <linux/tracehook.h>
#include <linux/tracehook.h>
#include <linux/audit.h>
#include <linux/audit.h>
#include <linux/seccomp.h>
#include <linux/seccomp.h>
...
@@ -490,6 +491,93 @@ enum mips_regset {
...
@@ -490,6 +491,93 @@ enum mips_regset {
REGSET_FPR
,
REGSET_FPR
,
};
};
struct
pt_regs_offset
{
const
char
*
name
;
int
offset
;
};
#define REG_OFFSET_NAME(reg, r) { \
.name = #reg, \
.offset = offsetof(struct pt_regs, r) \
}
#define REG_OFFSET_END { \
.name = NULL, \
.offset = 0 \
}
static
const
struct
pt_regs_offset
regoffset_table
[]
=
{
REG_OFFSET_NAME
(
r0
,
regs
[
0
]),
REG_OFFSET_NAME
(
r1
,
regs
[
1
]),
REG_OFFSET_NAME
(
r2
,
regs
[
2
]),
REG_OFFSET_NAME
(
r3
,
regs
[
3
]),
REG_OFFSET_NAME
(
r4
,
regs
[
4
]),
REG_OFFSET_NAME
(
r5
,
regs
[
5
]),
REG_OFFSET_NAME
(
r6
,
regs
[
6
]),
REG_OFFSET_NAME
(
r7
,
regs
[
7
]),
REG_OFFSET_NAME
(
r8
,
regs
[
8
]),
REG_OFFSET_NAME
(
r9
,
regs
[
9
]),
REG_OFFSET_NAME
(
r10
,
regs
[
10
]),
REG_OFFSET_NAME
(
r11
,
regs
[
11
]),
REG_OFFSET_NAME
(
r12
,
regs
[
12
]),
REG_OFFSET_NAME
(
r13
,
regs
[
13
]),
REG_OFFSET_NAME
(
r14
,
regs
[
14
]),
REG_OFFSET_NAME
(
r15
,
regs
[
15
]),
REG_OFFSET_NAME
(
r16
,
regs
[
16
]),
REG_OFFSET_NAME
(
r17
,
regs
[
17
]),
REG_OFFSET_NAME
(
r18
,
regs
[
18
]),
REG_OFFSET_NAME
(
r19
,
regs
[
19
]),
REG_OFFSET_NAME
(
r20
,
regs
[
20
]),
REG_OFFSET_NAME
(
r21
,
regs
[
21
]),
REG_OFFSET_NAME
(
r22
,
regs
[
22
]),
REG_OFFSET_NAME
(
r23
,
regs
[
23
]),
REG_OFFSET_NAME
(
r24
,
regs
[
24
]),
REG_OFFSET_NAME
(
r25
,
regs
[
25
]),
REG_OFFSET_NAME
(
r26
,
regs
[
26
]),
REG_OFFSET_NAME
(
r27
,
regs
[
27
]),
REG_OFFSET_NAME
(
r28
,
regs
[
28
]),
REG_OFFSET_NAME
(
r29
,
regs
[
29
]),
REG_OFFSET_NAME
(
r30
,
regs
[
30
]),
REG_OFFSET_NAME
(
r31
,
regs
[
31
]),
REG_OFFSET_NAME
(
c0_status
,
cp0_status
),
REG_OFFSET_NAME
(
hi
,
hi
),
REG_OFFSET_NAME
(
lo
,
lo
),
#ifdef CONFIG_CPU_HAS_SMARTMIPS
REG_OFFSET_NAME
(
acx
,
acx
),
#endif
REG_OFFSET_NAME
(
c0_badvaddr
,
cp0_badvaddr
),
REG_OFFSET_NAME
(
c0_cause
,
cp0_cause
),
REG_OFFSET_NAME
(
c0_epc
,
cp0_epc
),
#ifdef CONFIG_MIPS_MT_SMTC
REG_OFFSET_NAME
(
c0_tcstatus
,
cp0_tcstatus
),
#endif
#ifdef CONFIG_CPU_CAVIUM_OCTEON
REG_OFFSET_NAME
(
mpl0
,
mpl
[
0
]),
REG_OFFSET_NAME
(
mpl1
,
mpl
[
1
]),
REG_OFFSET_NAME
(
mpl2
,
mpl
[
2
]),
REG_OFFSET_NAME
(
mtp0
,
mtp
[
0
]),
REG_OFFSET_NAME
(
mtp1
,
mtp
[
1
]),
REG_OFFSET_NAME
(
mtp2
,
mtp
[
2
]),
#endif
REG_OFFSET_END
,
};
/**
* regs_query_register_offset() - query register offset from its name
* @name: the name of a register
*
* regs_query_register_offset() returns the offset of a register in struct
* pt_regs from its name. If the name is invalid, this returns -EINVAL;
*/
int
regs_query_register_offset
(
const
char
*
name
)
{
const
struct
pt_regs_offset
*
roff
;
for
(
roff
=
regoffset_table
;
roff
->
name
!=
NULL
;
roff
++
)
if
(
!
strcmp
(
roff
->
name
,
name
))
return
roff
->
offset
;
return
-
EINVAL
;
}
#if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32)
#if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32)
static
const
struct
user_regset
mips_regsets
[]
=
{
static
const
struct
user_regset
mips_regsets
[]
=
{
...
...
arch/mips/kernel/signal.c
View file @
40e084a5
...
@@ -21,6 +21,7 @@
...
@@ -21,6 +21,7 @@
#include <linux/wait.h>
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/unistd.h>
#include <linux/uprobes.h>
#include <linux/compiler.h>
#include <linux/compiler.h>
#include <linux/syscalls.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/uaccess.h>
...
@@ -856,6 +857,9 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
...
@@ -856,6 +857,9 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
user_exit
();
user_exit
();
if
(
thread_info_flags
&
_TIF_UPROBE
)
uprobe_notify_resume
(
regs
);
/* deal with pending signal delivery */
/* deal with pending signal delivery */
if
(
thread_info_flags
&
_TIF_SIGPENDING
)
if
(
thread_info_flags
&
_TIF_SIGPENDING
)
do_signal
(
regs
);
do_signal
(
regs
);
...
...
arch/mips/kernel/traps.c
View file @
40e084a5
...
@@ -984,6 +984,18 @@ asmlinkage void do_bp(struct pt_regs *regs)
...
@@ -984,6 +984,18 @@ asmlinkage void do_bp(struct pt_regs *regs)
* pertain to them.
* pertain to them.
*/
*/
switch
(
bcode
)
{
switch
(
bcode
)
{
case
BRK_UPROBE
:
if
(
notify_die
(
DIE_UPROBE
,
"uprobe"
,
regs
,
bcode
,
current
->
thread
.
trap_nr
,
SIGTRAP
)
==
NOTIFY_STOP
)
goto
out
;
else
break
;
case
BRK_UPROBE_XOL
:
if
(
notify_die
(
DIE_UPROBE_XOL
,
"uprobe_xol"
,
regs
,
bcode
,
current
->
thread
.
trap_nr
,
SIGTRAP
)
==
NOTIFY_STOP
)
goto
out
;
else
break
;
case
BRK_KPROBE_BP
:
case
BRK_KPROBE_BP
:
if
(
notify_die
(
DIE_BREAK
,
"debug"
,
regs
,
bcode
,
if
(
notify_die
(
DIE_BREAK
,
"debug"
,
regs
,
bcode
,
current
->
thread
.
trap_nr
,
SIGTRAP
)
==
NOTIFY_STOP
)
current
->
thread
.
trap_nr
,
SIGTRAP
)
==
NOTIFY_STOP
)
...
...
arch/mips/kernel/uprobes.c
0 → 100644
View file @
40e084a5
#include <linux/highmem.h>
#include <linux/kdebug.h>
#include <linux/types.h>
#include <linux/notifier.h>
#include <linux/sched.h>
#include <linux/uprobes.h>
#include <asm/branch.h>
#include <asm/cpu-features.h>
#include <asm/ptrace.h>
#include <asm/inst.h>
static
inline
int
insn_has_delay_slot
(
const
union
mips_instruction
insn
)
{
switch
(
insn
.
i_format
.
opcode
)
{
/*
* jr and jalr are in r_format format.
*/
case
spec_op
:
switch
(
insn
.
r_format
.
func
)
{
case
jalr_op
:
case
jr_op
:
return
1
;
}
break
;
/*
* This group contains:
* bltz_op, bgez_op, bltzl_op, bgezl_op,
* bltzal_op, bgezal_op, bltzall_op, bgezall_op.
*/
case
bcond_op
:
switch
(
insn
.
i_format
.
rt
)
{
case
bltz_op
:
case
bltzl_op
:
case
bgez_op
:
case
bgezl_op
:
case
bltzal_op
:
case
bltzall_op
:
case
bgezal_op
:
case
bgezall_op
:
case
bposge32_op
:
return
1
;
}
break
;
/*
* These are unconditional and in j_format.
*/
case
jal_op
:
case
j_op
:
case
beq_op
:
case
beql_op
:
case
bne_op
:
case
bnel_op
:
case
blez_op
:
/* not really i_format */
case
blezl_op
:
case
bgtz_op
:
case
bgtzl_op
:
return
1
;
/*
* And now the FPA/cp1 branch instructions.
*/
case
cop1_op
:
#ifdef CONFIG_CPU_CAVIUM_OCTEON
case
lwc2_op
:
/* This is bbit0 on Octeon */
case
ldc2_op
:
/* This is bbit032 on Octeon */
case
swc2_op
:
/* This is bbit1 on Octeon */
case
sdc2_op
:
/* This is bbit132 on Octeon */
#endif
return
1
;
}
return
0
;
}
/**
* arch_uprobe_analyze_insn - instruction analysis including validity and fixups.
* @mm: the probed address space.
* @arch_uprobe: the probepoint information.
* @addr: virtual address at which to install the probepoint
* Return 0 on success or a -ve number on error.
*/
int
arch_uprobe_analyze_insn
(
struct
arch_uprobe
*
aup
,
struct
mm_struct
*
mm
,
unsigned
long
addr
)
{
union
mips_instruction
inst
;
/*
* For the time being this also blocks attempts to use uprobes with
* MIPS16 and microMIPS.
*/
if
(
addr
&
0x03
)
return
-
EINVAL
;
inst
.
word
=
aup
->
insn
[
0
];
aup
->
ixol
[
0
]
=
aup
->
insn
[
insn_has_delay_slot
(
inst
)];
aup
->
ixol
[
1
]
=
UPROBE_BRK_UPROBE_XOL
;
/* NOP */
return
0
;
}
/**
* is_trap_insn - check if the instruction is a trap variant
* @insn: instruction to be checked.
* Returns true if @insn is a trap variant.
*
* This definition overrides the weak definition in kernel/events/uprobes.c.
* and is needed for the case where an architecture has multiple trap
* instructions (like PowerPC or MIPS). We treat BREAK just like the more
* modern conditional trap instructions.
*/
bool
is_trap_insn
(
uprobe_opcode_t
*
insn
)
{
union
mips_instruction
inst
;
inst
.
word
=
*
insn
;
switch
(
inst
.
i_format
.
opcode
)
{
case
spec_op
:
switch
(
inst
.
r_format
.
func
)
{
case
break_op
:
case
teq_op
:
case
tge_op
:
case
tgeu_op
:
case
tlt_op
:
case
tltu_op
:
case
tne_op
:
return
1
;
}
break
;
case
bcond_op
:
/* Yes, really ... */
switch
(
inst
.
u_format
.
rt
)
{
case
teqi_op
:
case
tgei_op
:
case
tgeiu_op
:
case
tlti_op
:
case
tltiu_op
:
case
tnei_op
:
return
1
;
}
break
;
}
return
0
;
}
#define UPROBE_TRAP_NR ULONG_MAX
/*
* arch_uprobe_pre_xol - prepare to execute out of line.
* @auprobe: the probepoint information.
* @regs: reflects the saved user state of current task.
*/
int
arch_uprobe_pre_xol
(
struct
arch_uprobe
*
aup
,
struct
pt_regs
*
regs
)
{
struct
uprobe_task
*
utask
=
current
->
utask
;
union
mips_instruction
insn
;
/*
* Now find the EPC where to resume after the breakpoint has been
* dealt with. This may require emulation of a branch.
*/
aup
->
resume_epc
=
regs
->
cp0_epc
+
4
;
if
(
insn_has_delay_slot
((
union
mips_instruction
)
aup
->
insn
[
0
]))
{
unsigned
long
epc
;
epc
=
regs
->
cp0_epc
;
__compute_return_epc_for_insn
(
regs
,
insn
);
aup
->
resume_epc
=
regs
->
cp0_epc
;
}
utask
->
autask
.
saved_trap_nr
=
current
->
thread
.
trap_nr
;
current
->
thread
.
trap_nr
=
UPROBE_TRAP_NR
;
regs
->
cp0_epc
=
current
->
utask
->
xol_vaddr
;
return
0
;
}
int
arch_uprobe_post_xol
(
struct
arch_uprobe
*
aup
,
struct
pt_regs
*
regs
)
{
struct
uprobe_task
*
utask
=
current
->
utask
;
current
->
thread
.
trap_nr
=
utask
->
autask
.
saved_trap_nr
;
regs
->
cp0_epc
=
aup
->
resume_epc
;
return
0
;
}
/*
* If xol insn itself traps and generates a signal(Say,
* SIGILL/SIGSEGV/etc), then detect the case where a singlestepped
* instruction jumps back to its own address. It is assumed that anything
* like do_page_fault/do_trap/etc sets thread.trap_nr != -1.
*
* arch_uprobe_pre_xol/arch_uprobe_post_xol save/restore thread.trap_nr,
* arch_uprobe_xol_was_trapped() simply checks that ->trap_nr is not equal to
* UPROBE_TRAP_NR == -1 set by arch_uprobe_pre_xol().
*/
bool
arch_uprobe_xol_was_trapped
(
struct
task_struct
*
tsk
)
{
if
(
tsk
->
thread
.
trap_nr
!=
UPROBE_TRAP_NR
)
return
true
;
return
false
;
}
int
arch_uprobe_exception_notify
(
struct
notifier_block
*
self
,
unsigned
long
val
,
void
*
data
)
{
struct
die_args
*
args
=
data
;
struct
pt_regs
*
regs
=
args
->
regs
;
/* regs == NULL is a kernel bug */
if
(
WARN_ON
(
!
regs
))
return
NOTIFY_DONE
;
/* We are only interested in userspace traps */
if
(
!
user_mode
(
regs
))
return
NOTIFY_DONE
;
switch
(
val
)
{
case
DIE_BREAK
:
if
(
uprobe_pre_sstep_notifier
(
regs
))
return
NOTIFY_STOP
;
break
;
case
DIE_UPROBE_XOL
:
if
(
uprobe_post_sstep_notifier
(
regs
))
return
NOTIFY_STOP
;
default:
break
;
}
return
0
;
}
/*
* This function gets called when XOL instruction either gets trapped or
* the thread has a fatal signal. Reset the instruction pointer to its
* probed address for the potential restart or for post mortem analysis.
*/
void
arch_uprobe_abort_xol
(
struct
arch_uprobe
*
aup
,
struct
pt_regs
*
regs
)
{
struct
uprobe_task
*
utask
=
current
->
utask
;
instruction_pointer_set
(
regs
,
utask
->
vaddr
);
}
unsigned
long
arch_uretprobe_hijack_return_addr
(
unsigned
long
trampoline_vaddr
,
struct
pt_regs
*
regs
)
{
unsigned
long
ra
;
ra
=
regs
->
regs
[
31
];
/* Replace the return address with the trampoline address */
regs
->
regs
[
31
]
=
ra
;
return
ra
;
}
/**
* set_swbp - store breakpoint at a given address.
* @auprobe: arch specific probepoint information.
* @mm: the probed process address space.
* @vaddr: the virtual address to insert the opcode.
*
* For mm @mm, store the breakpoint instruction at @vaddr.
* Return 0 (success) or a negative errno.
*
* This version overrides the weak version in kernel/events/uprobes.c.
* It is required to handle MIPS16 and microMIPS.
*/
int
__weak
set_swbp
(
struct
arch_uprobe
*
auprobe
,
struct
mm_struct
*
mm
,
unsigned
long
vaddr
)
{
return
uprobe_write_opcode
(
mm
,
vaddr
,
UPROBE_SWBP_INSN
);
}
/**
* set_orig_insn - Restore the original instruction.
* @mm: the probed process address space.
* @auprobe: arch specific probepoint information.
* @vaddr: the virtual address to insert the opcode.
*
* For mm @mm, restore the original opcode (opcode) at @vaddr.
* Return 0 (success) or a negative errno.
*
* This overrides the weak version in kernel/events/uprobes.c.
*/
int
set_orig_insn
(
struct
arch_uprobe
*
auprobe
,
struct
mm_struct
*
mm
,
unsigned
long
vaddr
)
{
return
uprobe_write_opcode
(
mm
,
vaddr
,
*
(
uprobe_opcode_t
*
)
&
auprobe
->
orig_inst
[
0
].
word
);
}
void
__weak
arch_uprobe_copy_ixol
(
struct
page
*
page
,
unsigned
long
vaddr
,
void
*
src
,
unsigned
long
len
)
{
void
*
kaddr
;
/* Initialize the slot */
kaddr
=
kmap_atomic
(
page
);
memcpy
(
kaddr
+
(
vaddr
&
~
PAGE_MASK
),
src
,
len
);
kunmap_atomic
(
kaddr
);
/*
* The MIPS version of flush_icache_range will operate safely on
* user space addresses and more importantly, it doesn't require a
* VMA argument.
*/
flush_icache_range
(
vaddr
,
vaddr
+
len
);
}
/**
* uprobe_get_swbp_addr - compute address of swbp given post-swbp regs
* @regs: Reflects the saved state of the task after it has hit a breakpoint
* instruction.
* Return the address of the breakpoint instruction.
*
* This overrides the weak version in kernel/events/uprobes.c.
*/
unsigned
long
uprobe_get_swbp_addr
(
struct
pt_regs
*
regs
)
{
return
instruction_pointer
(
regs
);
}
/*
* See if the instruction can be emulated.
* Returns true if instruction was emulated, false otherwise.
*
* For now we always emulate so this function just returns 0.
*/
bool
arch_uprobe_skip_sstep
(
struct
arch_uprobe
*
auprobe
,
struct
pt_regs
*
regs
)
{
return
0
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment