Message ID | 1572919114-3886-2-git-send-email-vincent.chen@sifive.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | riscv: add support for restartable sequence | expand |
Hi Vincent, On Tue, 5 Nov 2019, Vincent Chen wrote: > In order to select HAVE_REGS_AND_STACK_ACCESS_API, adding the APIs > required by kprobes to access pt_regs and stack entries to the RISC-V > ports. > > Signed-off-by: Patrick Stählin <me@packi.ch> > Signed-off-by: Vincent Chen <vincent.chen@sifive.com> As I understand it, this patch hasn't been signed off on by Patrick. I've sent him an E-mail asking him whether he's willing to add his Signed-off-by:, but haven't heard back from it. From our discussions, I understand that this patch is based partially on some of his earlier, public, kprobes work. In lieu of any response from Patrick, could you please resend this patch and just note in the commit description that it's partially based on one of his patches, add a Link: line that points to the URL of the patch that it's partially based on, and replace the Signed-off-by: with a Co-developed-by: or something similar? thanks, - Paul
On Thu, 21 Nov 2019, Paul Walmsley wrote: > As I understand it, this patch hasn't been signed off on by Patrick. > I've sent him an E-mail asking him whether he's willing to add his > Signed-off-by:, but haven't heard back from it. > > From our discussions, I understand that this patch is based partially on > some of his earlier, public, kprobes work. In lieu of any response from > Patrick, could you please resend this patch and just note in the commit > description that it's partially based on one of his patches, add a Link: > line that points to the URL of the patch that it's partially based on, and > replace the Signed-off-by: with a Co-developed-by: or something similar? OK - just looked at the patches more closely, and I think I see what's going on here. This patch looks like a rebased version of this patch: https://lore.kernel.org/linux-riscv/20181113195804.22825-2-me@packi.ch/ So let's just plan to use an updated version of Patrick's original (below). Please let me know if you have any comments on it or if I missed something - - Paul From: Patrick Stählin <me@packi.ch> Date: Tue, 13 Nov 2018 20:58:03 +0100 Subject: [PATCH] RISC-V: Implement ptrace regs and stack API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Needed for kprobes support. Copied and adapted from arm64 code. Signed-off-by: Patrick Stählin <me@packi.ch> [paul.walmsley@sifive.com: updated to apply; fixed checkpatch issues; fixed regs_get_register() kerneldoc] Cc: Vincent Chen <vincent.chen@sifive.com> Link: https://lore.kernel.org/linux-riscv/20181113195803.CjtBCsUcG9czwiqmPBGKUjvl5Ojxq2SIPaioQUHXFI0@z/ Link: https://lore.kernel.org/linux-riscv/1572919114-3886-1-git-send-email-vincent.chen@sifive.com/T/#mdb346527d25ea1959ab57ff9d1c056bcd29c7172 Signed-off-by: Paul Walmsley <paul.walmsley@sifive.com> --- arch/riscv/Kconfig | 1 + arch/riscv/include/asm/ptrace.h | 30 ++++++++++ arch/riscv/kernel/ptrace.c | 99 +++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 8eebbc8860bb..d5bbf4223fd2 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -61,6 +61,7 @@ config RISCV select SPARSEMEM_STATIC if 32BIT select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU select HAVE_ARCH_MMAP_RND_BITS + select HAVE_REGS_AND_STACK_ACCESS_API config ARCH_MMAP_RND_BITS_MIN default 18 if 64BIT diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h index d48d1e13973c..50d37b123a61 100644 --- a/arch/riscv/include/asm/ptrace.h +++ b/arch/riscv/include/asm/ptrace.h @@ -8,6 +8,7 @@ #include <uapi/asm/ptrace.h> #include <asm/csr.h> +#include <linux/compiler.h> #ifndef __ASSEMBLY__ @@ -60,6 +61,7 @@ struct pt_regs { #define user_mode(regs) (((regs)->sstatus & SR_SPP) == 0) +#define MAX_REG_OFFSET offsetof(struct pt_regs, orig_a0) /* Helpers for working with the instruction pointer */ static inline unsigned long instruction_pointer(struct pt_regs *regs) @@ -85,6 +87,12 @@ static inline void user_stack_pointer_set(struct pt_regs *regs, regs->sp = val; } +/* Valid only for Kernel mode traps. */ +static inline unsigned long kernel_stack_pointer(struct pt_regs *regs) +{ + return regs->sp; +} + /* Helpers for working with the frame pointer */ static inline unsigned long frame_pointer(struct pt_regs *regs) { @@ -101,6 +109,28 @@ static inline unsigned long regs_return_value(struct pt_regs *regs) return regs->a0; } +int regs_query_register_offset(const char *name); +unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, + unsigned int n); + +/** + * regs_get_register() - get register value from its offset + * @regs: pt_regs from which register value is gotten + * @offset: offset of the register. + * + * regs_get_register() returns the value from @regs of a register + * identified by @offset. The @offset is the offset of the register + * in struct pt_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); +} #endif /* __ASSEMBLY__ */ #endif /* _ASM_RISCV_PTRACE_H */ diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 1252113ef8b2..5076b30fe18b 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -125,6 +125,105 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task) return &riscv_user_native_view; } +struct pt_regs_offset { + const char *name; + int offset; +}; + +#define REG_OFFSET_NAME(r) {.name = #r, .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(sepc), + REG_OFFSET_NAME(ra), + REG_OFFSET_NAME(sp), + REG_OFFSET_NAME(gp), + REG_OFFSET_NAME(tp), + REG_OFFSET_NAME(t0), + REG_OFFSET_NAME(t1), + REG_OFFSET_NAME(t2), + REG_OFFSET_NAME(s0), + REG_OFFSET_NAME(s1), + REG_OFFSET_NAME(a0), + REG_OFFSET_NAME(a1), + REG_OFFSET_NAME(a2), + REG_OFFSET_NAME(a3), + REG_OFFSET_NAME(a4), + REG_OFFSET_NAME(a5), + REG_OFFSET_NAME(a6), + REG_OFFSET_NAME(a7), + REG_OFFSET_NAME(s2), + REG_OFFSET_NAME(s3), + REG_OFFSET_NAME(s4), + REG_OFFSET_NAME(s5), + REG_OFFSET_NAME(s6), + REG_OFFSET_NAME(s7), + REG_OFFSET_NAME(s8), + REG_OFFSET_NAME(s9), + REG_OFFSET_NAME(s10), + REG_OFFSET_NAME(s11), + REG_OFFSET_NAME(t3), + REG_OFFSET_NAME(t4), + REG_OFFSET_NAME(t5), + REG_OFFSET_NAME(t6), + REG_OFFSET_NAME(sstatus), + REG_OFFSET_NAME(sbadaddr), + REG_OFFSET_NAME(scause), + REG_OFFSET_NAME(orig_a0), + 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; roff++) + if (!strcmp(roff->name, name)) + return roff->offset; + return -EINVAL; +} + +/** + * 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 bool 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. + */ +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; +} + void ptrace_disable(struct task_struct *child) { clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
On Fri, Nov 22, 2019 at 6:32 AM Paul Walmsley <paul.walmsley@sifive.com> wrote: > > On Thu, 21 Nov 2019, Paul Walmsley wrote: > > > As I understand it, this patch hasn't been signed off on by Patrick. > > I've sent him an E-mail asking him whether he's willing to add his > > Signed-off-by:, but haven't heard back from it. > > > > From our discussions, I understand that this patch is based partially on > > some of his earlier, public, kprobes work. In lieu of any response from > > Patrick, could you please resend this patch and just note in the commit > > description that it's partially based on one of his patches, add a Link: > > line that points to the URL of the patch that it's partially based on, and > > replace the Signed-off-by: with a Co-developed-by: or something similar? > > OK - just looked at the patches more closely, and I think I see what's > going on here. This patch looks like a rebased version of this patch: > > https://lore.kernel.org/linux-riscv/20181113195804.22825-2-me@packi.ch/ > > So let's just plan to use an updated version of Patrick's original > (below). Please let me know if you have any comments on it or if I > missed something - > > > - Paul > Dear Paul, Due to the rule of mail classification, I do not find this mail in time. I am sorry for that. I think it is good for me. Thanks and regards Vincent > From: Patrick Stählin <me@packi.ch> > Date: Tue, 13 Nov 2018 20:58:03 +0100 > Subject: [PATCH] RISC-V: Implement ptrace regs and stack API > MIME-Version: 1.0 > Content-Type: text/plain; charset=UTF-8 > Content-Transfer-Encoding: 8bit > > Needed for kprobes support. Copied and adapted from arm64 code. > > Signed-off-by: Patrick Stählin <me@packi.ch> > [paul.walmsley@sifive.com: updated to apply; fixed checkpatch issues; > fixed regs_get_register() kerneldoc] > Cc: Vincent Chen <vincent.chen@sifive.com> > Link: https://lore.kernel.org/linux-riscv/20181113195803.CjtBCsUcG9czwiqmPBGKUjvl5Ojxq2SIPaioQUHXFI0@z/ > Link: https://lore.kernel.org/linux-riscv/1572919114-3886-1-git-send-email-vincent.chen@sifive.com/T/#mdb346527d25ea1959ab57ff9d1c056bcd29c7172 > Signed-off-by: Paul Walmsley <paul.walmsley@sifive.com> > --- > arch/riscv/Kconfig | 1 + > arch/riscv/include/asm/ptrace.h | 30 ++++++++++ > arch/riscv/kernel/ptrace.c | 99 +++++++++++++++++++++++++++++++++ > 3 files changed, 130 insertions(+) > > diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig > index 8eebbc8860bb..d5bbf4223fd2 100644 > --- a/arch/riscv/Kconfig > +++ b/arch/riscv/Kconfig > @@ -61,6 +61,7 @@ config RISCV > select SPARSEMEM_STATIC if 32BIT > select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU > select HAVE_ARCH_MMAP_RND_BITS > + select HAVE_REGS_AND_STACK_ACCESS_API > > config ARCH_MMAP_RND_BITS_MIN > default 18 if 64BIT > diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h > index d48d1e13973c..50d37b123a61 100644 > --- a/arch/riscv/include/asm/ptrace.h > +++ b/arch/riscv/include/asm/ptrace.h > @@ -8,6 +8,7 @@ > > #include <uapi/asm/ptrace.h> > #include <asm/csr.h> > +#include <linux/compiler.h> > > #ifndef __ASSEMBLY__ > > @@ -60,6 +61,7 @@ struct pt_regs { > > #define user_mode(regs) (((regs)->sstatus & SR_SPP) == 0) > > +#define MAX_REG_OFFSET offsetof(struct pt_regs, orig_a0) > > /* Helpers for working with the instruction pointer */ > static inline unsigned long instruction_pointer(struct pt_regs *regs) > @@ -85,6 +87,12 @@ static inline void user_stack_pointer_set(struct pt_regs *regs, > regs->sp = val; > } > > +/* Valid only for Kernel mode traps. */ > +static inline unsigned long kernel_stack_pointer(struct pt_regs *regs) > +{ > + return regs->sp; > +} > + > /* Helpers for working with the frame pointer */ > static inline unsigned long frame_pointer(struct pt_regs *regs) > { > @@ -101,6 +109,28 @@ static inline unsigned long regs_return_value(struct pt_regs *regs) > return regs->a0; > } > > +int regs_query_register_offset(const char *name); > +unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, > + unsigned int n); > + > +/** > + * regs_get_register() - get register value from its offset > + * @regs: pt_regs from which register value is gotten > + * @offset: offset of the register. > + * > + * regs_get_register() returns the value from @regs of a register > + * identified by @offset. The @offset is the offset of the register > + * in struct pt_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); > +} > #endif /* __ASSEMBLY__ */ > > #endif /* _ASM_RISCV_PTRACE_H */ > diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c > index 1252113ef8b2..5076b30fe18b 100644 > --- a/arch/riscv/kernel/ptrace.c > +++ b/arch/riscv/kernel/ptrace.c > @@ -125,6 +125,105 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task) > return &riscv_user_native_view; > } > > +struct pt_regs_offset { > + const char *name; > + int offset; > +}; > + > +#define REG_OFFSET_NAME(r) {.name = #r, .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(sepc), > + REG_OFFSET_NAME(ra), > + REG_OFFSET_NAME(sp), > + REG_OFFSET_NAME(gp), > + REG_OFFSET_NAME(tp), > + REG_OFFSET_NAME(t0), > + REG_OFFSET_NAME(t1), > + REG_OFFSET_NAME(t2), > + REG_OFFSET_NAME(s0), > + REG_OFFSET_NAME(s1), > + REG_OFFSET_NAME(a0), > + REG_OFFSET_NAME(a1), > + REG_OFFSET_NAME(a2), > + REG_OFFSET_NAME(a3), > + REG_OFFSET_NAME(a4), > + REG_OFFSET_NAME(a5), > + REG_OFFSET_NAME(a6), > + REG_OFFSET_NAME(a7), > + REG_OFFSET_NAME(s2), > + REG_OFFSET_NAME(s3), > + REG_OFFSET_NAME(s4), > + REG_OFFSET_NAME(s5), > + REG_OFFSET_NAME(s6), > + REG_OFFSET_NAME(s7), > + REG_OFFSET_NAME(s8), > + REG_OFFSET_NAME(s9), > + REG_OFFSET_NAME(s10), > + REG_OFFSET_NAME(s11), > + REG_OFFSET_NAME(t3), > + REG_OFFSET_NAME(t4), > + REG_OFFSET_NAME(t5), > + REG_OFFSET_NAME(t6), > + REG_OFFSET_NAME(sstatus), > + REG_OFFSET_NAME(sbadaddr), > + REG_OFFSET_NAME(scause), > + REG_OFFSET_NAME(orig_a0), > + 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; roff++) > + if (!strcmp(roff->name, name)) > + return roff->offset; > + return -EINVAL; > +} > + > +/** > + * 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 bool 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. > + */ > +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; > +} > + > void ptrace_disable(struct task_struct *child) > { > clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); > -- > 2.24.0.rc0
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 8eebbc8860bb..d5bbf4223fd2 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -61,6 +61,7 @@ config RISCV select SPARSEMEM_STATIC if 32BIT select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU select HAVE_ARCH_MMAP_RND_BITS + select HAVE_REGS_AND_STACK_ACCESS_API config ARCH_MMAP_RND_BITS_MIN default 18 if 64BIT diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h index d48d1e13973c..635b7b5506ec 100644 --- a/arch/riscv/include/asm/ptrace.h +++ b/arch/riscv/include/asm/ptrace.h @@ -8,6 +8,7 @@ #include <uapi/asm/ptrace.h> #include <asm/csr.h> +#include <linux/compiler.h> #ifndef __ASSEMBLY__ @@ -59,7 +60,7 @@ struct pt_regs { #endif #define user_mode(regs) (((regs)->sstatus & SR_SPP) == 0) - +#define MAX_REG_OFFSET offsetof(struct pt_regs, orig_a0) /* Helpers for working with the instruction pointer */ static inline unsigned long instruction_pointer(struct pt_regs *regs) @@ -79,6 +80,12 @@ static inline unsigned long user_stack_pointer(struct pt_regs *regs) { return regs->sp; } + +static inline unsigned long kernel_stack_pointer(struct pt_regs *regs) +{ + return regs->sp; +} + static inline void user_stack_pointer_set(struct pt_regs *regs, unsigned long val) { @@ -101,6 +108,26 @@ static inline unsigned long regs_return_value(struct pt_regs *regs) return regs->a0; } +int regs_query_register_offset(const char *name); + +unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n); +/** + * regs_get_register() - get register value from its offset + * @regs: pt_regs from which register value is gotten + * @offset: offset of the register. + * + * regs_get_register returns the value of a register whose offset from @regs. + * The @offset is the offset of the register in struct pt_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); +} #endif /* __ASSEMBLY__ */ #endif /* _ASM_RISCV_PTRACE_H */ diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 1252113ef8b2..2ae450d67659 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -125,6 +125,105 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task) return &riscv_user_native_view; } +struct pt_regs_offset { + const char *name; + int offset; +}; + +#define REG_OFFSET_NAME(r) {.name = #r, .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(sepc), + REG_OFFSET_NAME(ra), + REG_OFFSET_NAME(sp), + REG_OFFSET_NAME(gp), + REG_OFFSET_NAME(tp), + REG_OFFSET_NAME(t0), + REG_OFFSET_NAME(t1), + REG_OFFSET_NAME(t2), + REG_OFFSET_NAME(s0), + REG_OFFSET_NAME(s1), + REG_OFFSET_NAME(a0), + REG_OFFSET_NAME(a1), + REG_OFFSET_NAME(a2), + REG_OFFSET_NAME(a3), + REG_OFFSET_NAME(a4), + REG_OFFSET_NAME(a5), + REG_OFFSET_NAME(a6), + REG_OFFSET_NAME(a7), + REG_OFFSET_NAME(s2), + REG_OFFSET_NAME(s3), + REG_OFFSET_NAME(s4), + REG_OFFSET_NAME(s5), + REG_OFFSET_NAME(s6), + REG_OFFSET_NAME(s7), + REG_OFFSET_NAME(s8), + REG_OFFSET_NAME(s9), + REG_OFFSET_NAME(s10), + REG_OFFSET_NAME(s11), + REG_OFFSET_NAME(t3), + REG_OFFSET_NAME(t4), + REG_OFFSET_NAME(t5), + REG_OFFSET_NAME(t6), + REG_OFFSET_NAME(sstatus), + REG_OFFSET_NAME(sbadaddr), + REG_OFFSET_NAME(scause), + REG_OFFSET_NAME(orig_a0), + 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; +} + +/** + * 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 bool 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. + */ +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; +} + void ptrace_disable(struct task_struct *child) { clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);