2005-04-16 16:20:36 -06:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* Copyright (C) 1994, 95, 96, 97, 98, 99, 2000 by Ralf Baechle
|
|
|
|
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
|
|
|
|
*/
|
|
|
|
#ifndef _ASM_PTRACE_H
|
|
|
|
#define _ASM_PTRACE_H
|
|
|
|
|
|
|
|
|
2008-09-23 01:11:26 -06:00
|
|
|
#include <linux/compiler.h>
|
2005-04-16 16:20:36 -06:00
|
|
|
#include <linux/linkage.h>
|
2008-10-25 02:30:35 -06:00
|
|
|
#include <linux/types.h>
|
2006-09-06 10:00:22 -06:00
|
|
|
#include <asm/isadep.h>
|
2015-07-29 14:44:53 -06:00
|
|
|
#include <asm/page.h>
|
|
|
|
#include <asm/thread_info.h>
|
2012-10-09 02:47:14 -06:00
|
|
|
#include <uapi/asm/ptrace.h>
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2013-05-22 16:35:56 -06:00
|
|
|
/*
|
|
|
|
* 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.
|
2015-07-29 14:44:53 -06:00
|
|
|
*
|
|
|
|
* If you add a register here, also add it to regoffset_table[] in
|
|
|
|
* arch/mips/kernel/ptrace.c.
|
2013-05-22 16:35:56 -06:00
|
|
|
*/
|
|
|
|
struct pt_regs {
|
|
|
|
#ifdef CONFIG_32BIT
|
|
|
|
/* Pad bytes for argument save space on the stack. */
|
MIPS: O32/32-bit: Fix bug which can cause incorrect system call restarts
On 32-bit/O32, pt_regs has a padding area at the beginning into which the
syscall arguments passed via the user stack are copied. 4 arguments
totalling 16 bytes are copied to offset 16 bytes into this area, however
the area is only 24 bytes long. This means the last 2 arguments overwrite
pt_regs->regs[{0,1}].
If a syscall function returns an error, handle_sys stores the original
syscall number in pt_regs->regs[0] for syscall restart. signal.c checks
whether regs[0] is non-zero, if it is it will check whether the syscall
return value is one of the ERESTART* codes to see if it must be
restarted.
Should a syscall be made that results in a non-zero value being copied
off the user stack into regs[0], and then returns a positive (non-error)
value that matches one of the ERESTART* error codes, this can be mistaken
for requiring a syscall restart.
While the possibility for this to occur has always existed, it is made
much more likely to occur by commit 46e12c07b3b9 ("MIPS: O32 / 32-bit:
Always copy 4 stack arguments."), since now every syscall will copy 4
arguments and overwrite regs[0], rather than just those with 7 or 8
arguments.
Since that commit, booting Debian under a 32-bit MIPS kernel almost
always results in a hang early in boot, due to a wait4 syscall returning
a PID that matches one of the ERESTART* codes, which then causes an
incorrect restart of the syscall.
The problem is fixed by increasing the size of the padding area so that
arguments copied off the stack will not overwrite pt_regs->regs[{0,1}].
Signed-off-by: Alex Smith <alex.smith@imgtec.com>
Cc: <stable@vger.kernel.org> # v3.13+
Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
Tested-by: Aurelien Jarno <aurelien@aurel32.net>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/7454/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2014-07-23 07:40:11 -06:00
|
|
|
unsigned long pad0[8];
|
2013-05-22 16:35:56 -06:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Saved main processor registers. */
|
|
|
|
unsigned long regs[32];
|
|
|
|
|
|
|
|
/* Saved special registers. */
|
|
|
|
unsigned long cp0_status;
|
|
|
|
unsigned long hi;
|
|
|
|
unsigned long lo;
|
|
|
|
#ifdef CONFIG_CPU_HAS_SMARTMIPS
|
|
|
|
unsigned long acx;
|
|
|
|
#endif
|
|
|
|
unsigned long cp0_badvaddr;
|
|
|
|
unsigned long cp0_cause;
|
|
|
|
unsigned long cp0_epc;
|
|
|
|
#ifdef CONFIG_CPU_CAVIUM_OCTEON
|
2015-01-15 06:11:05 -07:00
|
|
|
unsigned long long mpl[6]; /* MTM{0-5} */
|
|
|
|
unsigned long long mtp[6]; /* MTP{0-5} */
|
2013-05-22 16:35:56 -06:00
|
|
|
#endif
|
2015-07-29 14:44:53 -06:00
|
|
|
unsigned long __last[0];
|
2013-05-22 16:35:56 -06:00
|
|
|
} __aligned(8);
|
|
|
|
|
2015-07-29 14:44:53 -06:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-09-23 01:11:26 -06:00
|
|
|
struct task_struct;
|
|
|
|
|
2014-07-23 07:40:13 -06:00
|
|
|
extern int ptrace_getregs(struct task_struct *child,
|
|
|
|
struct user_pt_regs __user *data);
|
|
|
|
extern int ptrace_setregs(struct task_struct *child,
|
|
|
|
struct user_pt_regs __user *data);
|
2008-10-11 09:18:57 -06:00
|
|
|
|
|
|
|
extern int ptrace_getfpregs(struct task_struct *child, __u32 __user *data);
|
|
|
|
extern int ptrace_setfpregs(struct task_struct *child, __u32 __user *data);
|
|
|
|
|
2008-09-23 01:11:26 -06:00
|
|
|
extern int ptrace_get_watch_regs(struct task_struct *child,
|
|
|
|
struct pt_watch_regs __user *addr);
|
|
|
|
extern int ptrace_set_watch_regs(struct task_struct *child,
|
|
|
|
struct pt_watch_regs __user *addr);
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
/*
|
|
|
|
* Does the process account for user or for system time?
|
|
|
|
*/
|
|
|
|
#define user_mode(regs) (((regs)->cp0_status & KU_MASK) == KU_USER)
|
|
|
|
|
2012-01-03 12:23:06 -07:00
|
|
|
static inline int is_syscall_success(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
return !regs->regs[7];
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline long regs_return_value(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
if (is_syscall_success(regs))
|
|
|
|
return regs->regs[2];
|
|
|
|
else
|
|
|
|
return -regs->regs[2];
|
|
|
|
}
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
#define instruction_pointer(regs) ((regs)->cp0_epc)
|
|
|
|
#define profile_pc(regs) instruction_pointer(regs)
|
|
|
|
|
2014-01-22 07:40:03 -07:00
|
|
|
extern asmlinkage long syscall_trace_enter(struct pt_regs *regs, long syscall);
|
2011-05-19 02:21:29 -06:00
|
|
|
extern asmlinkage void syscall_trace_leave(struct pt_regs *regs);
|
2005-04-16 16:20:36 -06:00
|
|
|
|
2012-01-12 18:17:21 -07:00
|
|
|
extern void die(const char *, struct pt_regs *) __noreturn;
|
2006-12-10 08:02:17 -07:00
|
|
|
|
2010-04-25 22:53:10 -06:00
|
|
|
static inline void die_if_kernel(const char *str, struct pt_regs *regs)
|
2006-12-10 08:02:17 -07:00
|
|
|
{
|
|
|
|
if (unlikely(!user_mode(regs)))
|
|
|
|
die(str, regs);
|
|
|
|
}
|
|
|
|
|
2012-10-09 13:16:07 -06:00
|
|
|
#define current_pt_regs() \
|
|
|
|
({ \
|
|
|
|
unsigned long sp = (unsigned long)__builtin_frame_address(0); \
|
|
|
|
(struct pt_regs *)((sp | (THREAD_SIZE - 1)) + 1 - 32) - 1; \
|
|
|
|
})
|
|
|
|
|
2012-08-17 00:22:04 -06:00
|
|
|
/* Helpers for working with the user stack pointer */
|
|
|
|
|
|
|
|
static inline unsigned long user_stack_pointer(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
return regs->regs[29];
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void user_stack_pointer_set(struct pt_regs *regs,
|
|
|
|
unsigned long val)
|
|
|
|
{
|
|
|
|
regs->regs[29] = val;
|
|
|
|
}
|
|
|
|
|
2005-04-16 16:20:36 -06:00
|
|
|
#endif /* _ASM_PTRACE_H */
|