forked from Minki/linux
dacbe41f77
While in theory user_enable_single_step/user_disable_single_step/ user_enable_blockstep could also be provided as an inline or macro there's no good reason to do so, and having the prototype in one places keeps code size and confusion down. Roland said: The original thought there was that user_enable_single_step() et al might well be only an instruction or three on a sane machine (as if we have any of those!), and since there is only one call site inlining would be beneficial. But I agree that there is no strong reason to care about inlining it. As to the arch changes, there is only one thought I'd add to the record. It was always my thinking that for an arch where PTRACE_SINGLESTEP does text-modifying breakpoint insertion, user_enable_single_step() should not be provided. That is, arch_has_single_step()=>true means that there is an arch facility with "pure" semantics that does not have any unexpected side effects. Inserting a breakpoint might do very unexpected strange things in multi-threaded situations. Aside from that, it is a peculiar side effect that user_{enable,disable}_single_step() should cause COW de-sharing of text pages and so forth. For PTRACE_SINGLESTEP, all these peculiarities are the status quo ante for that arch, so having arch_ptrace() itself do those is one thing. But for building other things in the future, it is nicer to have a uniform "pure" semantics that arch-independent code can expect. OTOH, all such arch issues are really up to the arch maintainer. As of today, there is nothing but ptrace using user_enable_single_step() et al so it's a distinction without a practical difference. If/when there are other facilities that use user_enable_single_step() and might care, the affected arch's can revisit the question when someone cares about the quality of the arch support for said new facility. Signed-off-by: Christoph Hellwig <hch@lst.de> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Roland McGrath <roland@redhat.com> Acked-by: David Howells <dhowells@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
303 lines
7.1 KiB
C
303 lines
7.1 KiB
C
#ifndef _ASM_X86_PTRACE_H
|
|
#define _ASM_X86_PTRACE_H
|
|
|
|
#include <linux/compiler.h> /* For __user */
|
|
#include <asm/ptrace-abi.h>
|
|
#include <asm/processor-flags.h>
|
|
|
|
#ifdef __KERNEL__
|
|
#include <asm/segment.h>
|
|
#include <asm/page_types.h>
|
|
#endif
|
|
|
|
#ifndef __ASSEMBLY__
|
|
|
|
#ifdef __i386__
|
|
/* this struct defines the way the registers are stored on the
|
|
stack during a system call. */
|
|
|
|
#ifndef __KERNEL__
|
|
|
|
struct pt_regs {
|
|
long ebx;
|
|
long ecx;
|
|
long edx;
|
|
long esi;
|
|
long edi;
|
|
long ebp;
|
|
long eax;
|
|
int xds;
|
|
int xes;
|
|
int xfs;
|
|
int xgs;
|
|
long orig_eax;
|
|
long eip;
|
|
int xcs;
|
|
long eflags;
|
|
long esp;
|
|
int xss;
|
|
};
|
|
|
|
#else /* __KERNEL__ */
|
|
|
|
struct pt_regs {
|
|
unsigned long bx;
|
|
unsigned long cx;
|
|
unsigned long dx;
|
|
unsigned long si;
|
|
unsigned long di;
|
|
unsigned long bp;
|
|
unsigned long ax;
|
|
unsigned long ds;
|
|
unsigned long es;
|
|
unsigned long fs;
|
|
unsigned long gs;
|
|
unsigned long orig_ax;
|
|
unsigned long ip;
|
|
unsigned long cs;
|
|
unsigned long flags;
|
|
unsigned long sp;
|
|
unsigned long ss;
|
|
};
|
|
|
|
#endif /* __KERNEL__ */
|
|
|
|
#else /* __i386__ */
|
|
|
|
#ifndef __KERNEL__
|
|
|
|
struct pt_regs {
|
|
unsigned long r15;
|
|
unsigned long r14;
|
|
unsigned long r13;
|
|
unsigned long r12;
|
|
unsigned long rbp;
|
|
unsigned long rbx;
|
|
/* arguments: non interrupts/non tracing syscalls only save upto here*/
|
|
unsigned long r11;
|
|
unsigned long r10;
|
|
unsigned long r9;
|
|
unsigned long r8;
|
|
unsigned long rax;
|
|
unsigned long rcx;
|
|
unsigned long rdx;
|
|
unsigned long rsi;
|
|
unsigned long rdi;
|
|
unsigned long orig_rax;
|
|
/* end of arguments */
|
|
/* cpu exception frame or undefined */
|
|
unsigned long rip;
|
|
unsigned long cs;
|
|
unsigned long eflags;
|
|
unsigned long rsp;
|
|
unsigned long ss;
|
|
/* top of stack page */
|
|
};
|
|
|
|
#else /* __KERNEL__ */
|
|
|
|
struct pt_regs {
|
|
unsigned long r15;
|
|
unsigned long r14;
|
|
unsigned long r13;
|
|
unsigned long r12;
|
|
unsigned long bp;
|
|
unsigned long bx;
|
|
/* arguments: non interrupts/non tracing syscalls only save upto here*/
|
|
unsigned long r11;
|
|
unsigned long r10;
|
|
unsigned long r9;
|
|
unsigned long r8;
|
|
unsigned long ax;
|
|
unsigned long cx;
|
|
unsigned long dx;
|
|
unsigned long si;
|
|
unsigned long di;
|
|
unsigned long orig_ax;
|
|
/* end of arguments */
|
|
/* cpu exception frame or undefined */
|
|
unsigned long ip;
|
|
unsigned long cs;
|
|
unsigned long flags;
|
|
unsigned long sp;
|
|
unsigned long ss;
|
|
/* top of stack page */
|
|
};
|
|
|
|
#endif /* __KERNEL__ */
|
|
#endif /* !__i386__ */
|
|
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
#include <linux/init.h>
|
|
|
|
struct cpuinfo_x86;
|
|
struct task_struct;
|
|
|
|
extern unsigned long profile_pc(struct pt_regs *regs);
|
|
|
|
extern unsigned long
|
|
convert_ip_to_linear(struct task_struct *child, struct pt_regs *regs);
|
|
extern void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
|
|
int error_code, int si_code);
|
|
void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
|
|
|
|
extern long syscall_trace_enter(struct pt_regs *);
|
|
extern void syscall_trace_leave(struct pt_regs *);
|
|
|
|
static inline unsigned long regs_return_value(struct pt_regs *regs)
|
|
{
|
|
return regs->ax;
|
|
}
|
|
|
|
/*
|
|
* user_mode_vm(regs) determines whether a register set came from user mode.
|
|
* This is true if V8086 mode was enabled OR if the register set was from
|
|
* protected mode with RPL-3 CS value. This tricky test checks that with
|
|
* one comparison. Many places in the kernel can bypass this full check
|
|
* if they have already ruled out V8086 mode, so user_mode(regs) can be used.
|
|
*/
|
|
static inline int user_mode(struct pt_regs *regs)
|
|
{
|
|
#ifdef CONFIG_X86_32
|
|
return (regs->cs & SEGMENT_RPL_MASK) == USER_RPL;
|
|
#else
|
|
return !!(regs->cs & 3);
|
|
#endif
|
|
}
|
|
|
|
static inline int user_mode_vm(struct pt_regs *regs)
|
|
{
|
|
#ifdef CONFIG_X86_32
|
|
return ((regs->cs & SEGMENT_RPL_MASK) | (regs->flags & X86_VM_MASK)) >=
|
|
USER_RPL;
|
|
#else
|
|
return user_mode(regs);
|
|
#endif
|
|
}
|
|
|
|
static inline int v8086_mode(struct pt_regs *regs)
|
|
{
|
|
#ifdef CONFIG_X86_32
|
|
return (regs->flags & X86_VM_MASK);
|
|
#else
|
|
return 0; /* No V86 mode support in long mode */
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* X86_32 CPUs don't save ss and esp if the CPU is already in kernel mode
|
|
* when it traps. The previous stack will be directly underneath the saved
|
|
* registers, and 'sp/ss' won't even have been saved. Thus the '®s->sp'.
|
|
*
|
|
* This is valid only for kernel mode traps.
|
|
*/
|
|
static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
|
|
{
|
|
#ifdef CONFIG_X86_32
|
|
return (unsigned long)(®s->sp);
|
|
#else
|
|
return regs->sp;
|
|
#endif
|
|
}
|
|
|
|
static inline unsigned long instruction_pointer(struct pt_regs *regs)
|
|
{
|
|
return regs->ip;
|
|
}
|
|
|
|
static inline unsigned long frame_pointer(struct pt_regs *regs)
|
|
{
|
|
return regs->bp;
|
|
}
|
|
|
|
static inline unsigned long user_stack_pointer(struct pt_regs *regs)
|
|
{
|
|
return regs->sp;
|
|
}
|
|
|
|
/* Query offset/name of register from its name/offset */
|
|
extern int regs_query_register_offset(const char *name);
|
|
extern const char *regs_query_register_name(unsigned int offset);
|
|
#define MAX_REG_OFFSET (offsetof(struct pt_regs, ss))
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
#define arch_has_single_step() (1)
|
|
#ifdef CONFIG_X86_DEBUGCTLMSR
|
|
#define arch_has_block_step() (1)
|
|
#else
|
|
#define arch_has_block_step() (boot_cpu_data.x86 >= 6)
|
|
#endif
|
|
|
|
#define ARCH_HAS_USER_SINGLE_STEP_INFO
|
|
|
|
struct user_desc;
|
|
extern int do_get_thread_area(struct task_struct *p, int idx,
|
|
struct user_desc __user *info);
|
|
extern int do_set_thread_area(struct task_struct *p, int idx,
|
|
struct user_desc __user *info, int can_allocate);
|
|
|
|
#ifdef CONFIG_X86_PTRACE_BTS
|
|
extern void ptrace_bts_untrace(struct task_struct *tsk);
|
|
|
|
#define arch_ptrace_untrace(tsk) ptrace_bts_untrace(tsk)
|
|
#endif /* CONFIG_X86_PTRACE_BTS */
|
|
|
|
#endif /* __KERNEL__ */
|
|
|
|
#endif /* !__ASSEMBLY__ */
|
|
|
|
#endif /* _ASM_X86_PTRACE_H */
|